commit d410ad0acbc22ea83e9e893cf7b930485d919ddf Author: Steve Howes Date: Tue Jul 25 16:17:48 2023 +0100 Initial commit of 1.11 source as released by PC Engines Original source https://www.pcengines.ch/file/wbios111s.zip diff --git a/CHANGE.TXT b/CHANGE.TXT new file mode 100644 index 0000000..6d07db0 --- /dev/null +++ b/CHANGE.TXT @@ -0,0 +1,105 @@ +tinyBIOS change notes Pascal Dornier +--------------------- pdornier@pcengines.com + +BUGS: + +- PS/2 mouse support needs more work, file available on request only. + +- CD Boot support needs more work, file available on request only. + +- Support for PCI bridges does not work right. + +A386 BUGS (version 4.05): + +- low(forward defined) not filled in correctly. + +- use32 causes offset 16 to be clobbered. STARTOFS should be > 0. + +HISTORY: + +This covers core changes only, not chipsets / board ports. +Refer to respective source files for details on those. + +; new version + +pd 030304 DATA add 4 drive structure, IDE I/O and master slave info + + HDD add HD_INFO, 4 drive support + +; released as 1.3c + +pd 010413 DATA add d_extop -> top of extended memory. + + INT1X Fix Int15 function 88 to handle memory > 64MB, + add Int15 function E8. + + POST2 Fix CMOS RAM to handle memory > 64MB, write + d_extop. + + EQU Add safeorg macro to properly flag code overruns. + +; released as 1.3b + +pd 010413 FDD add STPC patch FD_VERF000 -> simulate verify by reading + to F000 segment. + +; released as 1.3a + +pd 010214 KBD change reset jump to F000:FFF0 -> fixes AMD Elan SC520 + Ctrl-Alt-Del restart. + +pd 010207 KBD skip keyboard init if NO_KBC option set, KBC not + present. + +pd 010126 all Ported to A386 assembler. + +; ancient history below + +pd 000816 KBD Fixed putbuf overrun prevention. + +pd 000815 EQU Changed from double use of m_vpal, m_vmsel to + VID m_console, m_conkey (unused floppy bytes) + + COM Disable 2400 baud mode (set by DOS during bootup) + if CONSOLE set. + +pd 000424 POST2 Add option QUICKMEM -> memory size and clear only. + +pd 000211 HDD Added support for new SanDisk CompactFlash cards. + +pd 991127 EQU Added variables for PS/2 mouse. + INT1X Added hooks for PS/2 mouse. + PS2MOUS Added PS/2 mouse support. + COM Fixed m_devflg handling. + SIO_NONE Moved to SIO directory. + SMC* Moved to SIO directory. + +pd 991115 PCIPNP fix PCI I/O allocation: some devices (e.g. ESS Tech) + have 16 bit base registers. + +pd 991020/21 DATA Add hd_top. + HDD Add hd_top, needed to support M-Systems DiskOnChip. + POST Add option for M-Systems DiskOnChip. + +pd 991003 LPT Fixed lp_test loop. + +pd 990603 POST Changed to RET_SP macro. + + AS.COM Added RET_SP macro to assembler. + Abandoned CYRIXGX mode in assembler (inconsistent + op codes depending on version). + Source for assembler is now available in PASM + directory. + + POST Added POST code and call for cs_cache + if present (needs to be moved from decide routine). + Added return label for cs_init, cs_det, allows + return by JMP rather than RET. + + FILL16 Changed from .COM to .BIN extension. + + FILL32 Changed from .COM to .BIN extension. + +pd 990525 PCI Rewrote 32 bit PCI BIOS to fix Linux problem. + +pd 990501 HDD Add CDBOOT hook. diff --git a/COM.8 b/COM.8 new file mode 100644 index 0000000..5e26852 --- /dev/null +++ b/COM.8 @@ -0,0 +1,328 @@ + ; + ; Serial BIOS + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; pd 050417 enable outside override for con_init + ; pd 010207 integrate serial console support here + ; pd 991127 fix m_devflg + ; + ; Serial console notes: + ; + ; Variable CONSOLE must be equal to the I/O port of the COM port used. + ; Define CONINT=3 or CONINT=4 to select the serial port interrupt. + ; VGA BIOS will take over Int 10, and inhibit serial console output. + +#if ! def COM_INIT ;default value: +COM_INIT equ 11100011xb ;9600 8N1 serial port initialization +#endif + ; + ; INT 14 entry + ; +int14: sti ;reenable interrupts + push ds ;save registers + push si + push dx + push cx + push bx + mov bx,ax ;save AX + xor ax,ax ;access BIOS segment + mov ds,ax + cmp dx,4 ;max port ? + jae rs_exit ;:return + mov si,dx ;-> table index + mov cl,[si.m_rstime] ;get time-out value + shl si,1 + mov dx,[si.m_rsio] ;get I/O port base + and dx,dx ;0 -> not present + jz rs_exit + mov al,bh ;get command code + and al,al + jz rs_init ;AH=0 -> initialize + dec ax + jz rs_xmit ;AH=1 -> transmit + dec ax + jz rs_recv ;AH=2 -> receive + dec ax + jz rs_stat ;AH=3 -> get status +rs_exit: pop bx ;restore registers + pop cx + pop dx + pop si + pop ds + iret + ; + ; AH=0: initialize serial port + ; +rs_init: mov al,bl ;AH=0; get baud rate + shr al,4 ;-> table index + and al,1110b +#if def CONSOLE + cmp dx,CONSOLE ;console ? + jz rs_stat ;if yes, don't let DOS change the + ;parameters +#endif +#if def COM_NO2400 ;lock out DOS 2400 baud setting... + cmp al,10 ;2400 baud ? + jz rs_stat ;yes - don't do it +#endif + mov si,ax + add dl,3 + mov al,80h ;base+3 DLAB=1: access baud rate register + out dx,al + dec dx + dec dx + mov ax,word [cs:si+rs_baud] ;get baudrate + out dx,al ;base+1 baudrate MSB + mov al,ah + dec dx + out dx,al ;base+0 baudrate LSB + add dl,3 + mov al,bl + and al,1fh ;set parameters + out dx,al ;base+3 + dec dx + dec dx +#if def CONSOLE ;don't disable interrupt on console + in al,dx ;base+1 + and al,1 + cmp dx,CONSOLE+1 + jz rs_init1 +#endif + mov al,0 ;disable interrupts +rs_init1: out dx,al ;base+1 + dec dx ;base+0 + ; + ; AH=3: get status + ; +rs_stat: add dl,5 + in al,dx + mov ah,al + inc dx + in al,dx + jmp rs_exit + ; + ; AH=1: transmit character + ; +rs_xmit: mov al,3 ;modem control: set DTR, RTS + add dl,4 + out dx,al + inc dx ;modem status: wait for DSR, CTS + inc dx + mov bh,30h + call rs_wait + jnz rs_xmtime ;:time-out + dec dx + mov bh,20h ;line status: wait for xmit ready + call rs_wait + jnz rs_xmtime + sub dl,5 + mov al,bl ;character to send + out dx,al ;send it + jmp rs_exit + ; + ; time-out error + ; +rs_xmtime: mov al,bl ;restore character +rs_rxtime: or ah,80h ;set time-out flag + jmp rs_exit + ; + ; AH=2: receive character + ; +rs_recv: mov al,1 ;modem control: set DTR + add dl,4 + out dx,al + inc dx ;modem status: wait for DSR + inc dx + mov bh,20h + call rs_wait + jnz rs_rxtime ;:time-out + dec dx + mov bh,1 ;line status: wait for receive data + call rs_wait + jnz rs_rxtime ;:time-out + and ah,1eh ;mask status + and dl,0f8h ;receive register + in al,dx ;read character + jmp rs_exit ;exit + ; + ; serial port wait + ; +rs_wait: mov ch,cl ;copy time-out value + xor si,si ;clear counter +rs_wait2: in al,dx + mov ah,al ;save status + and al,bh ;check status + cmp al,bh ;set ? + jz rs_wait9 ;:yes + dec si + jnz rs_wait2 ;:try again + dec ch + jnz rs_wait2 + or bh,bh ;time-out +rs_wait9: ret + ; + ; test & initialize serial ports + ; +rs_test: mov ax,0101h ;init serial port time-out + mov [m_rstime],ax + mov [m_rstime+2],ax + + mov di,m_rsio ;destination for I/O port value + mov si,offset rs_ports ;port addresses +rs_test1: lods word [cs:si] ;get port address + and ax,ax ;end of table ? + jz rs_test3 ;:end + xchg dx,ax ;AX -> DX, points to scratch register + mov ax,0aa55h + out dx,al + out iowait,ax ;invert bus + out iowait,ax + in al,dx + cmp al,55h ;pattern ok ? + jnz rs_test1 ;:no, port not present, try next + and dl,0f8h ;clear low bits of I/O address + mov [di],dx ;write port address + inc di + inc di + add byte ptr [m_devflg+1],2 ;increment serial port count + cmp di,m_rsio+8 ;space for more ports ? + jnz rs_test1 ;:yes +rs_test3: mov dx,3 ;init serial ports +rs_test4: mov ax,COM_INIT + int 14h + dec dx + jns rs_test4 + ret + ; + ; serial port console + ; +#if def CONSOLE +#if ! def con_init + +con_init: mov byte [m_conkey],0 ;clear key buffer + mov byte [m_console],0 ;disable serial console + + ; hook serial interrupt + +#if def CONINT + cli ;set INT 3 vector = COM2 + xor ax,ax + mov ds,ax +#if CONINT = 3 + mov word [11*4],con_int + mov word [11*4+2],cs +#endif +#if CONINT = 4 + mov word [12*4],con_int + mov word [12*4+2],cs +#endif +#if CONINT = 5 + mov word [13*4],con_int + mov word [13*4+2],cs +#endif + sti + + mov dx,CONSOLE + in al,dx ;clear receive buffer + out iowait,ax + mov dl,low(CONSOLE+1) + mov al,1 ;enable receive interrupt + out dx,al + out iowait,ax + mov dl,low(CONSOLE+4) ;enable interrupt driver + in al,dx + or al,8 + out dx,al + + in al,pic0+1 ;enable serial interrupt (int 3) +#if CONINT = 3 + and al,0f7 +#endif +#if CONINT = 4 + and al,0ef +#endif +#if CONINT = 5 + and al,0df +#endif + out iowait,ax + out pic0+1,al +#endif + mov byte [m_conkey],0 ;clear key buffer + mov byte [m_console],1 ;set serial console flag + ret +#endif ; con_init + + ; + ; serial interrupt handler for serial console + ; +con_int: pusha + push ds + xor ax,ax + mov ds,ax + + mov dx,CONSOLE ;read data + in al,dx + test byte [m_console],1 ;serial console enabled ? + jz con_int49 ;:no, ignore + + ; we need to handle Esc xx and Esc Esc sequences to emulate + ; function / cursor keys + + xor ah,ah ;clear high part of scan code + cmp al,27 ;escape ? + jz con_int41 + cmp byte [m_conkey],1 ;Esc previous character ? + jb con_int48 ;0: take this key + jz con_int43 ;store key, need another for sequence + mov ah,al ;current key -> AH + mov al,[m_conkey] ;previous key -> AL + mov si,offset ansitab + mov cx,(ansitab9-ansitab) / 4 +con_int44: cmp ax,[cs:si] ;compare with sequence + jz con_int45 + lea si,[si+4] ;skip to next + loop con_int44 ;try next + jmp short con_int47 ;not found - skip + +con_int45: mov ax,[cs:si+2] ;get scan code from table +con_int48: call putbuf ;place in keyboard buffer +con_int47: mov byte [m_conkey],0 ;clear key buffer + +con_int49: mov al,eoi ;end of interrupt + out pic0,al + pop ds + popa + iret + + ; handle escape + +con_int41: cmp byte [m_conkey],1 ;Esc previous key ? + jz con_int48 ;yes: enter escape + mov byte [m_conkey],1 ;set escape flag + jmp con_int49 + +con_int43: mov byte [m_conkey],al ;store key + jmp con_int49 ;skip + ; + ; ANSI to scan code translation table + ; + even +ansitab: db "OP",000,03b ;F1 + db "OQ",000,03c ;F2 + db "Ow",000,03d ;F3 + db "Ox",000,03e ;F4 + db "Ot",000,03f ;F5 + db "Ou",000,040 ;F6 + db "Oq",000,041 ;F7 + db "Or",000,042 ;F8 + db "Op",000,043 ;F9 (F10 gives same) + db "[A",000,048 ;cursor up + db "[B",000,050 ;cursor down + db "[D",000,04b ;cursor left + db "[C",000,04d ;cursor right + db "[H",000,047 ;home + db "[K",000,04f ;end +ansitab9: +#endif ;CONSOLE diff --git a/CPL0_5.HTM b/CPL0_5.HTM new file mode 100644 index 0000000..013ca00 --- /dev/null +++ b/CPL0_5.HTM @@ -0,0 +1,123 @@ + + + +Body + + + + + +

Common Public License - v 0.5 +

+

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. +

+

1. DEFINITIONS +

"Contribution" means: + +

+ + + + + + + + + + +

+

"Contributor" means any person or entity that distributes the Program. +

+

"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. +

+

"Program" means the Contributions distributed in accordance with this Agreement. +

+

"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. +

+

2. GRANT OF RIGHTS + +

+ + + + + + + + + + + + + + + + + + + + + + +

3. REQUIREMENTS +

A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +

+ + + + + + + + + + + + + + + + + + + +

When the Program is made available in source code form: + +

+ + + + +

+

Contributors may not remove or alter any copyright notices contained within the Program. +

+

Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. +

+

4. COMMERCIAL DISTRIBUTION +

Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. +

+

For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. +

+

5. NO WARRANTY +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. +

+

6. DISCLAIMER OF LIABILITY +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +

+

7. GENERAL +

If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. +

+

If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. +

+

All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. +

+

Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. +

+

This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. +

+

+ + + \ No newline at end of file diff --git a/DATA.8 b/DATA.8 new file mode 100644 index 0000000..ba29304 --- /dev/null +++ b/DATA.8 @@ -0,0 +1,105 @@ + ; + ; BIOS configuration data + ; + ; (C)1997-2003 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; This data is modified to store system configuration, such as + ; PCI data, hard disk parameters, etc. + ; + ; pd 050206 add Int15 E820 system memory map + ; pd 030304 add support for four drives, add IDE I/O port and + ; master / slave information + ; pd 991020 add hd_top + ; + even + db "_DAT" ;header for checksum utility + dw d_sum-d_beg ;pointer to checksum + +d_beg: ;start of data + ; + ; system memory map for Int15 E820 + ; +e820map: dd 0,0 ;index 0: base memory +d_basmem: dd 0,0 ;size of base memory + dd 1 ;type 1 = memory + + dd 0000f0000,0 ;index 1: BIOS ROM + dd 000010000,0 + dd 2 ;type 2 = reserved + + dd 0fff00000,0 ;index 2: high BIOS + dd 000100000,0 + dd 2 ;type 2 = reserved + + dd 000100000,0 ;index 3: extended memory +d_exmem: dd 0,0 ;size of extended memory + dd 1 ;type 1 = memory +e820mape: + ; + ; Hard disk parameters + ; +hd_prm0: db dpt_port dup 0 ;drive 80 + dw hdc ;primary port + db 0,0 ;dpt_dev master +hd_prm1: db dpt_port dup 0 ;drive 81 + dw hdc ;primary port + db 0,10h ;dpt_dev slave +#if def HD_4DRV + db dpt_port dup 0 ;drive 82 + dw hdc2 ;secondary port + db 0,0 ;dpt_dev master + db dpt_port dup 0 ;drive 83 + dw hdc2 ;secondary port + db 0,10h ;dpt_dev slave +hd_prm99: ;end of table +hd_top: db 84h ;top HDD + 1 +hd_good: db 01h ;set if last drive detected +#else +hd_prm99: +hd_top: db 82h ;top HDD + 1 +hd_good: db 01h ;set if last drive detected +#endif + ; + ; PCI data + ; + + ; this is a procedure to avoid problems in protected mode access... + +getlbus: db 0b0h ;MOV AL +d_lastbus: db 0 ;last PCI bus + ret + +#if def CDBOOT + even +d_cdlba: dw 0,0 ;base LBA for last session +d_cdbase: dw 0 ;CD-ROM port base +d_cddrv: db 0b0h ;slave drive +d_cdsec: db 15 ;sectors per track +d_cdflag: db 0 ;1 = enable CD emulation + +#endif + ; + ; Data checksum + ; +d_sum: db 0 ;checksum, end of data block + ;(filled in by utility) + ; + ; calculate data checksum + ; +d_dosum: +#if ! def M6117 + wbinvd ;write back cache +#endif + mov al,0 + mov si,offset d_beg + mov cx,d_sum-d_beg +d_dosum1: add al,[cs:si] ;calculate checksum + inc si + loop d_dosum1 + neg al + mov [cs:si],al +#if ! def M6117 + wbinvd +#endif + ret diff --git a/DEBUG.8 b/DEBUG.8 new file mode 100644 index 0000000..4c1bd8e --- /dev/null +++ b/DEBUG.8 @@ -0,0 +1,181 @@ + ; + ; Debug routines - don't include for production version + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + + ; + ; display hex word + ; +hex: push ax + mov al,ah + call hexb + pop ax +hex2: call hexb + mov al," " + jmp short putc + ; + ; display hex byte + ; +hexb: push ax + shr al,4 + call hexn + pop ax +hexn: and al,15 + cmp al,10 + jb hexn2 + add al,7 +hexn2: add al,"0" + ; + ; display character AL + ; +putc: mov ah,0eh + mov bh,0 + int 10h + ret + +#if def DEBUG ;disabled for normal use + ; + ; display a hex byte on first line of screen + ; +diaghex: push ds + push es + push ax + push di + xor di,di + mov ds,ax + mov ah,al ;save AL + mov di,vidseg + mov es,di + mov di,[tmp_diag] + mov word [es:di]," " ;clear current mark + cmp di,1000 ;156 ;limit ? + jb diaghex1 + xor di,di +diaghex1: inc di + inc di + shr al,4 ;display high nibble + call diaghex2 + mov al,ah ;display low nibble + call diaghex2 + + mov al,"*" ;mark current location + mov [es:di],al + mov [tmp_diag],di + + pop di + pop ax + pop es + pop ds + ret + +diaghex2: and al,15 ;display a byte + cmp al,10 + jb diaghex3 + add al,7 +diaghex3: add al,"0" + mov [es:di],al + inc di + inc di ;skip attribute byte + ret + ; + ; display word AX at screen location [BX] + ; +diagword: push ax + mov al,ah + call diagbyte + pop ax +diagbyte: push ax + shr al,4 + call diagnib + pop ax +diagnib: and al,15 + cmp al,10 + jb diagnib2 + add al,7 +diagnib2: add al,"0" + mov [bx],al + inc bx + inc bx + ret + ; + ; display registers [BP] + ; +v_dump: pusha +; mov ax,[bp._cs] +; call hex +; mov ax,[bp._ip] +; call hex + mov ax,[bp._ax] + call hex + mov ax,[bp._bx] + call hex + mov ax,[bp._cx] + call hex + mov ax,[bp._dx] + call hex + mov ax,[bp._es] + call hex + mov si,offset msg_arr + call v_msg + popa + ret + ; + ; display registers [BP] + ; +v_dump2: pusha + pushf + mov ax,[bp._ax] + call hex + mov ax,[bp._bx] + call hex + mov ax,[bp._cx] + call hex + mov ax,[bp._dx] + call hex + mov si,offset msg_cr + test byte [bp+18h],1 ;carry ? + jz v_dump3 ;no: ok + mov al,[m_fdfile] + call hex2 + mov al,[m_fdfile+1] + call hex2 + mov al,[m_fdfile+2] + call hex2 + mov si,offset msg_cy +v_dump3: call v_msg + popf + popa + ret + +msg_arr: db " -> ",0 +msg_cy: db " CY" +msg_cr: db 13,10,0 + ; + ; display CS:IP on video + ; +diag_csip: mov ax,vidseg ;increment video location + mov ds,ax + inc byte [158] ;end of first text line + + push bp ;display current address + mov bp,sp + push bx + mov ax,[bp+10] ;CS: + mov bx,0 + call diagword + mov byte [bx],":" + inc bx + inc bx + mov ax,[bp+8] ;IP + call diagword + pop bx + pop bp + ret + ; + ; Debug messages + ; +deb_dskch: db "Disk change",13,10,0 + +#endif ;DEBUG diff --git a/EQU.8 b/EQU.8 new file mode 100644 index 0000000..3db7eca --- /dev/null +++ b/EQU.8 @@ -0,0 +1,266 @@ + ; + ; BIOS equates + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + + ; + ; System board I/O ports + ; +dma0 equ 00h ;Primary 8237 DMA controller +pic0 equ 20h ;primary 8259 interrupt controller +timer equ 40h ;8254 timer + +#if ! def port61 +port61 equ 61h ;miscellaneous control +#endif + +kb_dat equ 60h ;keyboard data port +kb_stat equ 64h ;keyboard status / command port + +cm_idx equ 70h ;CMOS index port +cm_dat equ 71h ;CMOS data port + +post equ 80h ;POST code port +fd_page equ 81h ;DMA page register 1 (floppy) +port92 equ 92h ;Fast A20 gate / reset +pic1 equ 0a0h ;secondary 8259 interrupt controller +dma1 equ 0c0h ;Secondary 8237 DMA controller +iowait equ 0ebh ;I/O wait register +npx equ 0f0h ;x87 coprocessor + +hdc equ 01f0h ;primary hard disk +hdc2 equ 0170h ;secondary hard disk +hdc_dat equ 0 ;HD data (16 or 32 bit) +hdc_err equ 1 ;HD error (read only) +hdc_cnt equ 2 ;HD sector count +hdc_sec equ 3 ;HD sector +hdc_cyl equ 4 ;HD cylinder low +hdc_cyh equ 5 ;HD cylinder high +hdc_drv equ 6 ;HD device / head +hdc_cmd equ 7 ;HD command (write only) +hdc_stat equ 7 ;HD status (read only) +hdc_stat2 equ 03f6h-01f0h ;HD alternate status +hdc_ctrl equ 03f6h-01f0h ;HD reset, interrupt enable + +crtc equ 03b4h ;CRTC I/O port (monochrome) +crtc_cga equ 03d4h ;CRTC I/O port (color) +fdc equ 03f0h ;primary floppy controller +fdc_ctrl equ fdc+2 ;control register (drive select, motor) +fdc_stat equ fdc+4 ;FDC status +fdc_data equ fdc+5 ;FDC data +fdc_rate equ fdc+7 ;disk rate +fdc_chg equ fdc+7 ;disk change status (bit 7) +picedge0 equ 04d0h ;primary interrupts edge/level mode +picedge1 equ 04d1h ;secondary interrupts edge/level mode +pci_ad equ 0cf8h ;PCI address register (32 bit) +pci_adl equ low(pci_ad) +pci_dat equ 0cfch ;PCI data register (32 bit) + ;This is hardwired for $xxfc in + ;several places, look before you + ;change +pci_datl equ low(pci_dat) + ; + ; Interrupt vectors + ; +vec00 equ 0 +vec08 equ 08h*4 ;low hardware interrupts +vec10 equ 10h*4 ;BIOS interrupts +vec13 equ 13h*4 ;disk interrupt +vec40 equ 40h*4 ;vector to floppy interrupt +vec41 equ 41h*4 ;vector to drive 80 parameters +vec46 equ 46h*4 ;vector to drive 81 parameters +vec70 equ 70h*4 ;high hardware interrupts + ; + ; BIOS data area + ; + ; Most BIOSes access this as segment 0040, but I prefer 0000. + ; The only place where this can cause problems is the keyboard + ; buffer, which is offset to segment 0040. + ; +m_fdbase equ 0078h ;INT1E = disk base + + ; we use IRQ area instead of extended BIOS data area for the + ; PS/2 mouse variables. This can be moved anywhere. + +#if def PS2MOUSE +m_msbase equ 0200h ;base address +m_msvec equ m_msbase ;pointer to call-back routine +m_msflag equ m_msbase+4 ;mouse flags + ;bit 7 = 1: command pending + ;bit 6 = 1: FE request resend + ;bit 5 = 1: FA command acknowledge + ;bit 4 = 1: FC error condition + ;bit 3 = 1: call back installed + ;bit 2 = 1: initialized + ;bit 1..0 : buffer index +m_msbuf equ m_msbase+5 ;mouse buffer (3 bytes - 1) +m_msend equ m_msbase+7 +#endif + +m_rsio equ 0400h ;serial I/O ports +m_lpio equ 0408h ;parallel I/O ports +m_devflg equ 0410h ;device flags (word) +m_lomem equ 0413h ;low memory size (word) in 1KB units +m_kbf equ 0417h ;keyboard flag +m_kbf1 equ 0418h ;keyboard flag 2 +m_kbnum equ 0419h ;Alt-xxx numeric entry +m_kbhead equ 041ah ;^buffer head +m_kbtail equ 041ch ;^buffer tail +m_kbbuf equ 041eh ;keyboard buffer +m_kbbuf9 equ 043eh ;end of keyboard buffer +m_fdrecal equ 043eh ;floppy recal status +m_fdmot equ 043fh ;floppy motor status +m_fdcnt equ 0440h ;floppy motor timer +m_fdstat equ 0441h ;last floppy status +m_fdfile equ 0442h ;floppy status / command file +m_vmode equ 0449h ;video mode +m_vcol equ 044ah ;video columns (word) +m_vpgsz equ 044ch ;video page size (word) +m_vpage equ 044eh ;video page start address (word) +m_vcolrow equ 0450h ;video column, row (8 pages) +m_vcursor equ 0460h ;video cursor end, start line (word) +m_vpgno equ 0462h ;video page number +m_vcrt equ 0463h ;video CRTC I/O (word) +m_vmsel equ 0465h ;video mode select register + ;used for key buffer, option CONSOLE +m_vpal equ 0466h ;video CGA palette register + ;used for serial redirect flag, + ;option CONSOLE. +m_ioofs equ 0467h ;option ROM offset +m_ioseg equ 0469h ;option ROM segment +m_timer equ 046ch ;timer ticks since midnight (4 bytes) +m_timofl equ 0470h ;1 equ timer overflow +m_brkflg equ 0471h ;keyboard break flag +m_rstflg equ 0472h ;reset flag, $1234 if Ctrl-Alt-Del +m_hdstat equ 0474h ;HDD status last command +m_hdcnt equ 0475h ;number of HDD drives +m_lptime equ 0478h ;parallel port time-out (4 bytes) +m_rstime equ 047ch ;serial port time-out (4 bytes) +m_kbstart equ 0480h ;^buffer start +m_kbend equ 0482h ;^buffer end +m_vrow equ 0484h ;video row count - 1 +m_hdst equ 048ch ;HDD status register +m_hderr equ 048dh ;HDD error register +m_hdflag equ 048eh ;HDD interrupt flag +;m_fdinfo equ 048fh ;floppy info +m_console equ 048fh ;serial port console flag +m_fdmed0 equ 0490h ;floppy A: media status +;m_fdmed1 equ 0491h ;floppy B: media status +m_conkey equ 0492h ;console key buffer +;m_fdmed0a equ 0492h ;floppy A: initial media status +;m_fdmed1a equ 0493h ;floppy B: initial media status +m_fdtrk0 equ 0494h ;floppy A: current track +;m_fdtrk1 equ 0495h ;floppy B: current track +m_kbf3 equ 0496h ;keyboard status +m_kbf2 equ 0497h ;keyboard status +m_prtsc equ 0500h ;print screen status + ; + ; Temporary data area - cleared before boot + ; +tmp_diag equ 04b0h ;diagnostic hex dump display (16) + +tmp_losz equ 0504h ;base top of memory (32 bit) +tmp_hisz equ 0508h ;extended top of memory (32 bit) +tmp_kbd equ 0510h ;keyboard response, AA = ok (8) +tmp_kbc equ 0511h ;keyboard controller, FF = not present +tmp_npx equ 0514h ;control word, for init +tmp_kbfail equ 0518h ;1 if keyboard failure +tmp_rtc equ 0519h ;1 if RTC battery failure or bad time / + ;date +tmp_ss equ 051ah ;RTC second for run check +tmp_mm equ 051bh ;RTC minute for run check +tmp_timer equ 051ch ;Timer (16 bit) for timer run check +tmp_tim equ 051eh ;1 if timer / RTC update failure + +tmp_pci equ 0520h ;workspace for PCI BIOS + +tmp_stack equ 1000h ;POST stack + +tmp_buf equ 1000h ;temporary buffer, used for HDD init +tmp_buf2 equ 1200h ;extra buffer, define TEMPSIZE if using + ; + ; RTC / CMOS + ; +cm_nmi equ 80h ;NMI disable index +cm_ss equ 0 ;RTC seconds +cm_ssa equ 1 ;RTC seconds alarm +cm_mm equ 2 ;RTC minutes +cm_mma equ 3 ;RTC minutes alarm +cm_hh equ 4 ;RTC hours +cm_hha equ 5 ;RTC hours alarm +cm_day equ 6 ;RTC day of week (cleared, not used) +cm_dd equ 7 ;RTC day +cm_mo equ 8 ;RTC month +cm_yy equ 9 ;RTC year +cm_a equ 0ah ;RTC status, divider +cm_b equ 0bh ;RTC mode, interrupt control +cm_c equ 0ch ;RTC interrupt status +cm_d equ 0dh ;RTC battery status +cm_dia equ 0eh ;CMOS diagnostic byte (power fail flag) +cm_shut equ 0fh ;CMOS shutdown register +cm_test equ 2fh ;CMOS test register (legacy: checksum) +cm_exl equ 30h ;CMOS extended memory size, low +cm_exh equ 31h ;CMOS extended memory size, high +cm_cent equ 32h ;CMOS century +#if def CM_LEGACY +cm_meml equ 15h ;CMOS base memory, low +cm_memh equ 16h ;CMOS base memory, high +cm_exl2 equ 17h ;CMOS extended memory size, low +cm_exh2 equ 18h ;CMOS extended memory size, high +#endif + ; + ; Miscellaneous + ; +eoi equ 20h ;PIC: End of Interrupt +bofs equ 0400h ;BIOS segment offset + ; + ; TTY equates + ; +bell equ 7 ;bell +bs equ 8 ;backspace +lf equ 10 ;line feed +cr equ 13 ;carriage return +vidseg equ 0b000h ;video segment for monochrome +vid_fill equ 0720h ;fill pattern, space + ; + ; PUSHA stack frame + ; +_cs equ 22 ;return address (for debug) +_ip equ 20 +_ds equ 18 ;pushed before +_es equ 16 ;pushed before +_ax equ 14 +_al equ _ax +_ah equ _ax+1 +_bx equ 8 +_bl equ _bx +_bh equ _bx+1 +_cx equ 12 +_cl equ _cx +_ch equ _cx+1 +_dx equ 10 +_dl equ _dx +_dh equ _dx+1 +_si equ 2 +_di equ 0 +_bp equ 4 +_flags equ 6 + ; + ; RET_SP - subroutine call without stack + ; +ret_sp macro + mov sp,offset >m1 ;point to return address + jmp #1 ;jump to subroutine +m1: dw $+2 ;return address +#em + ; + ; SAFEORG - set code pointer with range check + ; +safeorg macro +##if $ GT #1 + error Code overrun - reduce STARTOFS or move include files ! +##endif + db (#1 - $) dup 0ffh ;fill empty space +#em diff --git a/FDD.8 b/FDD.8 new file mode 100644 index 0000000..cd8ace9 --- /dev/null +++ b/FDD.8 @@ -0,0 +1,752 @@ + ; + ; Floppy BIOS + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; Limitations: + ; + ; - Only 3.5" floppy drives supported. + ; - Only 1.44 MB mode supported. + ; - Only one drive supported. + ; +#if def DEBUG +FD_DEBUG: ;& enable debug code, comment out for + ;production code +#endif + ; + ; INT 13 entry + ; +int13: sti + push ds ;save registers + push es + pusha + mov bp,sp ;access to stack frame +#if def FD_DEBUG + call v_dump ;& dump registers +#endif + xor di,di ;access BIOS segment + mov ds,di + cmp ah,0 ;reset doesn't care about drive # + jz int13_1 + cmp dl,0 ;valid drive ? + jnz fd_badcmd ;:bail out +int13_1: cmp byte [m_fdmed0],0 ;drive doesn't exist ? + jz fd_badcmd ;:bail out + mov di,ax ;command -> index + shr di,8 + add di,di + and byte [bp+18h],0feh ;clear return carry + cmp di,19h*2 ;limit command vector + jae fd_badcmd + jmp [cs:di.fd_vectab] ;jump to command + ; + ; floppy vector table + ; +fd_vectab: dw fd_rst ;AH=00: recalibrate drive + dw fd_status ;AH=01: get status + dw fd_read ;AH=02: read + dw fd_write ;AH=03: write + dw fd_verify ;AH=04: verify + dw fd_format ;AH=05: format track + dw fd_badcmd ;AH=06: bad + dw fd_badcmd ;AH=07: bad + dw fd_drvprm ;AH=08: read drive parameters + dw fd_badcmd ;AH=09: bad + dw fd_badcmd ;AH=0A: bad + dw fd_badcmd ;AH=0B: bad + dw fd_badcmd ;AH=0C: bad + dw fd_badcmd ;AH=0D: bad + dw fd_badcmd ;AH=0E: bad + dw fd_badcmd ;AH=0F: bad + dw fd_badcmd ;AH=10: bad + dw fd_badcmd ;AH=11: bad + dw fd_badcmd ;AH=12: bad + dw fd_badcmd ;AH=13: bad + dw fd_badcmd ;AH=14: bad + dw fd_gettyp ;AH=15: get drive type + dw fd_dskchg ;AH=16: get disk change status + dw fd_fmttyp ;AH=17: set drive type for format + dw fd_medtyp ;AH=18: set media type for format + ; + ; Illegal command + ; +fd_badcmd: mov byte [m_fdstat],1 ;illegal command + ; + ; AH=01: get status + ; +fd_status: +fd_exit: mov al,[m_fdstat] ;get error code +fd_exit1: mov [bp._ah],al ;return in AH + and al,al ;error ? + jz fd_exit2 + or byte [bp+18h],1 ;yes: set carry +fd_exit2: +#if def FD_DEBUG + call v_dump2 ;& dump registers +#endif + popa ;restore registers + pop es + pop ds + iret ;return from interrupt + ; + ; AH=00: recalibrate floppy drives + ; +fd_rst: les si,[m_fdbase] + + in al,pic0+1 ;enable floppy interrupt + and al,0bfh + out iowait,al + out pic0+1,al + + mov dx,fdc_rate ;set 500 kb/s data rate + mov al,0 + out dx,al + + mov dx,fdc_ctrl + mov word [m_fdstat],0 ;clear error status + mov byte [m_fdcnt],0ffh ;prevent motor off event + mov al,[m_fdmot] ;digital output register + rol al,4 + and al,11110011b + or al,8 ;enable interrupts + out dx,al ;reset FDC + and byte [m_fdrecal],70h ;clear interrupt and recal flags + mov cx,100 ;wait a bit +fd_rstw: out iowait,ax + loop fd_rstw + xor al,4 ;end reset pulse + out dx,al + call fd_wait ;wait for interrupt + jb fd_rst1 ;:bad controller + + mov bl,0c0h ;expected result +fd_rst0: mov al,8 ;sense interrupt + call fd_cmd ;send command + jb fd_rst1 ;:error + call fd_res ;get results + jb fd_rst1 ;:error + cmp byte [m_fdfile],bl ;drive ready changed state ? + jnz fd_rst1 ;no: error + inc bl + cmp bl,0c4h ;done all drives ? + jb fd_rst0 + ; + ; FDC specify command + ; + mov al,3 ;specify command + call fd_cmd ;send to FDC + jb fd_spec9 + mov al,[es:si] ;m_fdbase -> step rate, head unload + call fd_cmd ;send to FDC + jb fd_spec9 + mov al,[es:si+1] ;m_fdbase -> motor on time, DMA mode + call fd_cmd + +#if def FDC_FIFO ;NSC PC87306 + jb fd_spec9 + mov al,13h ;configure command + call fd_cmd + jb fd_spec9 + mov al,0 + call fd_cmd + jb fd_spec9 + mov al,00001000xb ;enable FIFO + call fd_cmd + jb fd_spec9 + mov al,0 ;precompensation track + call fd_cmd +#endif + +fd_spec9: mov al,[es:si+2] ;m_fdbase -> motor count + mov [m_fdcnt],al ;restore motor timer + jmp fd_exit + +fd_rst1: or byte [m_fdstat],20h ;bad controller +fd_rst2: jmp fd_exit + ; + ; AH=05: format track + ; +fd_format: call fd_dma ;convert address ES:BX -> CH:DX + mov ax,0200h+4ah ;sectors * 4 shift, DMA mode write + call fd_dma2 ;complete DMA initialization + jb fd_rw90 ;:error + call fd_seek ;turn on motor, seek to track + jb fd_rw90 ;:error + call cs_waitbx ;wait BX milliseconds + + mov al,3 ;specify command + call fd_cmd ;send to FDC + jb fd_rw90 + mov al,[es:si] ;m_fdbase -> step rate, head unload + call fd_cmd ;send to FDC + jb fd_rw90 + mov al,[es:si+1] ;m_fdbase -> motor on time, DMA mode + call fd_cmd + jb fd_rw90 + + mov al,4dh ;format command + call fd_cmd + jb fd_rw90 + mov al,[bp._dh] ;head + shl al,2 + call fd_cmd + jb fd_rw90 + mov al,[es:si+3] ;bytes per sector + call fd_cmd + jb fd_rw90 + mov al,[es:si+4] ;sectors per track + call fd_cmd + jb fd_rw90 + mov al,[es:si+7] ;gap length + call fd_cmd + jb fd_rw90 + mov al,[es:si+8] ;fill byte + jmp fd_rw3 ;continue as read / write + ; + ; AH=03: write sectors + ; +fd_write: call fd_dma ;convert address ES:BX -> CH:DX + mov al,4ah ;DMA mode: write + call fd_dma2 ;complete DMA initialization + jb fd_rw90 ;:error + call fd_seek ;turn on motor, seek to track + jb fd_rw90 ;:error + call cs_waitbx ;wait BX milliseconds + mov al,0c5h ;write command + jmp short fd_rw1 + +fd_rw90: jmp fd_rw9 + ; + ; AH=04: verify + ; +fd_verify: +#if def FD_VERF000 ;STPC patch: read to F000 segment + mov bx,0f000h + mov es,bx + xor bx,bx +#endif + call fd_dma ;convert address ES:BX -> CH:DX + xor dx,dx ;clear offset -> no DMA errors +#if def FD_VERF000 + mov al,46h ;DMA mode: read +#else + mov al,42h ;DMA mode: verify +#endif + jmp short fd_read2 ;rest like read + ; + ; AH=02: read + ; +fd_read: call fd_dma ;convert address ES:BX -> CH:DX + mov al,46h ;DMA mode: read +fd_read2: call fd_dma2 ;complete DMA initialization + jb fd_rw90 ;:error + call fd_seek ;turn on motor, seek to track + jb fd_rw90 ;:error +fd_read3: mov al,0e6h ;FDC read command + ; + ; execute read / write command + ; +fd_rw1: call fd_cmd ;command + jb fd_rw9 ;:error + mov al,0 ;drive 0 + test byte [bp._dh],1 ;other head ? + jz fd_rw2 + or al,4 ;set head bit +fd_rw2: call fd_cmd + jb fd_rw9 + mov al,[bp._ch] ;track number + call fd_cmd + jb fd_rw9 + mov al,[bp._dh] ;head number + call fd_cmd + jb fd_rw9 + mov al,[bp._cl] ;sector number + call fd_cmd + jb fd_rw9 + mov al,[es:si+3] ;m_fdbase -> bytes / sector + call fd_cmd + jb fd_rw9 + mov al,[es:si+4] ;m_fdbase -> end of track sector + call fd_cmd + jb fd_rw9 + mov al,[es:si+5] ;m_fdbase -> gap length + call fd_cmd + jb fd_rw9 + mov al,[es:si+6] ;m_fdbase -> data length +fd_rw3: call fd_cmd + jb fd_rw9 + call fd_wait ;wait for interrupt + jb fd_rw9 + call fd_res ;get results + jb fd_rw9 + cmp byte [bp._al],3 ;write ? + jz fd_rw4 + cmp byte [bp._al],5 ;format ? + jnz fd_rw5 +fd_rw4: mov bx,1 ;wait 1 ms after write -> write gate + call cs_waitbx ;delay (prevent immediate step away) +fd_rw5: test byte [m_fdfile],0c0h ;any error ? + jz fd_rw99 ;:no + call fd_error ;translate error +fd_rw9: cmp byte [bp._ah],5 ;format ? + jz fd_rw99 + mov byte [bp._al],0 ;clear sector count +fd_rw99: mov al,[es:si+2] ;m_fdbase -> motor count + mov [m_fdcnt],al + jmp fd_exit + ; + ; translate error code + ; +fd_error: mov al,20h ;(FDC error) + test byte [m_fdfile],80h ;invalid command ? + jnz fd_err9 ;:yes + mov al,[m_fdfile+1] ;ST1 status + and al,10110111b ;mask off unused status bits + mov bx,fd_errtab+9 ;^error table + stc ;ensure termination +fd_err1: dec bx + rcr al,1 ;test bit + jnb fd_err1 ;try next + mov al,[cs:bx] ;get correct error code +fd_err9: or [m_fdstat],al + ret + ; + ; error codes + ; +fd_errtab: db 20h ;(bit overflow) + db 4 ;sector not found + db 20h ;(not used) + db 10h ;CRC error + db 8 ;DMA overrun + db 20h ;(not used) + db 4 ;sector not found + db 3 ;write protect + db 2 ;address mark not found + ; + ; Floppy parameters + ; +fd_ptab: db 0dfh ;step rate, head unload + db 02 ;head load, DMA mode + db 25h ;motor wait + db 02 ;512 bytes per sector + db 18 ;end of track + db 24h ;normal gap + db 0ffh ;DTL + db 54h ;gap length for format + db 0f6h ;fill byte for format + db 15 ;head settle time (x 1 ms) + db 8 ;motor start time (x 125 ms) + ; + ; turn on motor + ; + ; out: BX = turn-on delay in ms + ; +fd_motor: xor bx,bx ;no settling time required + + ; turn on motor if required + + mov byte [m_fdcnt],255 ;prevent motor shutdown + mov byte [m_fdstat],0 ;clear error code + test byte [m_fdmot],1 ;motor running ? + jnz fd_mot1 ;:yes + or byte [m_fdmot],1 ;set motor flag + mov dx,fdc_ctrl ;turn on motor + mov al,00011100xb ;drive 0, DMA enable, not reset + out dx,al + mov bl,[es:si+10] ;m_fdbase -> motor start time + shl bx,7 ;x 128 -> value in ms +fd_mot1: ret + ; + ; turn on motor, seek to track if needed + ; + ; -> settling time in ms in BX + ; +fd_seek: call fd_motor ;turn on motor + + ; check and reset disk change + + mov dx,fdc_chg ;disk change line active ? + in al,dx + and al,80h + jz fd_seek1x ;:no +#if def FD_DEBUG + pusha ;& write a message on disk change + mov si,deb_dskch ;& + call v_msg ;& + popa ;& +#endif + call fd_seek1z ;recalibrate + cmp byte [bp._ch],0 ;destination = track 0 ? + jnz fd_seek1b + inc byte [bp._ch] + call fd_seek2 ;seek to track 1 + dec byte [bp._ch] +fd_seek1b: call fd_seek2 ;seek to track 0 / wanted track + mov dx,fdc_chg ;disk change line still active ? + in al,dx + and al,80h + jz fd_seek1c ;no: ok + mov byte [m_fdstat],80h ;set time-out error +fd_seek1c: ret + + ; recalibrate if required + +fd_seek1x: test byte [m_fdrecal],1 ;need recalibration ? + jnz fd_seek2 ;:no +fd_seek1z: mov al,7 ;recalibrate + call fd_cmd + jb fd_seek9 + mov al,0 ;drive 0 + call fd_cmd + jb fd_seek9 + call fd_wsk ;wait for seek complete + jb fd_seek9 + or byte [m_fdrecal],1 ;set bit - done + mov byte [m_fdtrk0],0 ;set current track + or bx,bx ;motor settling time ? + jnz fd_seek2 ;yes, bigger than head settling time + mov bl,[es:si+9] ;m_fdbase -> head settling time + + ; seek to track if required + +fd_seek2: mov al,[bp._ch] ;destination track + cmp al,[m_fdtrk0] ;= current track ? + jz fd_seek9 ;:done (implied clc) + + mov al,0fh ;seek command + call fd_cmd + jb fd_seek9 + mov al,0 ;drive number + call fd_cmd + jb fd_seek9 + mov al,[bp._ch] ;destination track + call fd_cmd + jb fd_seek9 + mov [m_fdtrk0],al ;set new position + call fd_wsk ;wait for seek complete + + or bx,bx ;motor settling time ? + jnz fd_seek9 ;yes, bigger than head settling time + mov bl,[es:si+9] ;m_fdbase -> head settling time + +fd_seek9: ret + ; + ; wait for seek to complete, get status + ; +fd_wsk: call fd_wait ;wait for interrupt + jb fd_wsk8 ;:error +fd_wsk2: mov al,8 ;sense interrupt + call fd_cmd + jb fd_wsk8 + call fd_res ;get results + jb fd_wsk8 + mov al,[m_fdfile] + and al,01100000xb ;error, seek end bits + cmp al,00100000xb ;no error, seek end ? + jz fd_wsk9 ;:ok +fd_wsk8: or byte [m_fdstat],40h ;set seek error bit + stc +fd_wsk9: ret + ; + ; initialize DMA controller + ; + ; ES = buffer segment + ; BX = buffer offset + ; +fd_dma: ; ES:BX -> physical address CH:DX, init ES:SI, AH + + mov dx,es ;buffer segment + rol dx,4 + mov ch,dl ;high 4 bits + and dl,0f0h ;mask off low bits + add dx,bx ;add buffer offset + adc ch,0 + les si,[m_fdbase] ;load ^ floppy parameters + mov ah,[es:si+3] ;get shift count + add ah,7 ;shift count + 7 (128 byte base) + ret + ; + ; second half of DMA setup + ; + ; AL = DMA mode + ; AH = shift count (for sector -> byte calculation) + ; DX = address (low 16 bits) + ; CH = address (high 4 bits) + ; +fd_dma2: mov cl,ah ;shift count + mov bl,[bp._al] ;number of sectors + mov bh,0 + shl bx,cl ;-> byte count + dec bx ;-1 for DMA + add bx,dx ;test for DMA overflow + jb fd_dma9 ;:error + sub bx,dx ;restore count + +fd_dma3: cli ;critical section + push ax + mov al,6 ;disable DRQ2 + out dma0+10,al + out iowait,ax + pop ax + out dma0+12,al ;dummy access -> reset hi/lo FF + out iowait,ax + out dma0+11,al ;write DMA mode + out iowait,ax + mov al,dl ;low address + out dma0+4,al + out iowait,ax + mov al,dh ;high address + out dma0+4,al + mov al,ch ;DMA page register + and al,15 + out fd_page,al + +#if def GX_FDFIX + push dx + mov al,0 ;clear high page register !!! + mov dx,fd_page+0400h + out dx,al + pop dx +#endif + + mov al,bl ;low count + out dma0+5,al + out iowait,ax + mov al,bh ;high count + out dma0+5,al + out iowait,ax + mov al,2 ;enable DRQ2 + out dma0+10,al + + sti ;end of critical section + clc + ret + +fd_dma9: mov byte [m_fdstat],9 ;DMA overflow error + ret + ; + ; write command byte AL to FDC + ; +fd_cmd: mov ah,al ;save data + mov dx,fdc_stat + xor cx,cx +fd_cmd1: in al,dx ;read status + and al,0c0h ;RQM / DIO + xor al,80h ;RQM set, DIO clear = write to FDC + jz fd_cmd2 ;:ok + loop fd_cmd1 ;keep trying + or byte [m_fdstat],80h ;time out + stc + ret + +fd_cmd2: mov al,ah ;restore data + inc dx + out dx,al ;write data to fdc_data + ret ;(carry clear) + ; + ; get results from FDC + ; +fd_res: mov dx,fdc_stat + xor cx,cx + mov di,m_fdfile ;destination pointer +fd_res1: xor cx,cx ;clear time-out +fd_res2: out iowait,ax + in al,dx ;read status + cmp al,0c0h ;FDC ready for data read ? + jnb fd_res3 ;:yes + loop fd_res2 +fd_res8: or byte [m_fdstat],80h ;time out + stc + ret + +fd_res3: out iowait,ax + inc dx + in al,dx ;read fdc_data + dec dx + mov [di],al ;store result + inc di + out iowait,ax ;give FDC some time to update status + out iowait,ax + out iowait,ax + out iowait,ax + cmp di,m_fdfile+7 ;max bytes ? + jz fd_res9 ;:done + ; + ; wait for next byte + ; +fd_res4: in al,dx ;get controller status + cmp al,0c0h + jae fd_res3 ;:ready with data + and al,0f0h ;ready for next command ? + cmp al,80h + jz fd_res9 ;:yes, done + loop fd_res4 ;keep trying + jmp fd_res8 ;time-out + +fd_res9: clc + ret + ; + ; Floppy interrupt + ; +irq6: push ax + push ds + xor ax,ax + mov ds,ax + or byte [m_fdrecal],80h ;set interrupt flag + mov al,eoi ;end of interrupt + out pic0,al + pop ds + pop ax + iret + ; + ; Did we get interrupt ? CY set if time-out + ; +fd_wait: mov byte [m_fdcnt],0ffh ;keep motor running + push cx + mov cx,[m_timer] ;start time + add cx,19 ;time-out 1 second +fd_wait2: test byte [m_fdrecal],80h ;did we get interrupt ? + jnz fd_wait3 ;:yes + cmp cx,[m_timer] + js fd_wait4 ;:time-out + hlt ;wait for next interrupt + jmp fd_wait2 ;look again +fd_wait3: pop cx + and byte [m_fdrecal],7Fh ;clear interrupt flag + ret + +fd_wait4: or byte [m_fdstat],80h ;set time-out status + stc + pop cx + ret + ; + ; AH=08: read drive parameters + ; +fd_drvprm: xor bx,bx ;for non-existent drive + xor cx,cx + xor dx,dx + xor si,si + xor di,di + mov [m_fdstat],bl ;clear status + mov [bp._ax],bx ;clear AX + + cmp byte [m_fdmed0],0 ;drive present ? + jz fd_drvp9 ;:no + + mov si,cs ;segment -> ES + mov di,fd_ptab + mov bx,4 ;drive type = 1.44 MB + mov cx,79*256+18 ;80 tracks, 18 sectors per track + mov dx,1*256+1 ;1 head, 1 drive + +fd_drvp9: mov [bp._bx],bx + mov [bp._cx],cx + mov [bp._dx],dx + mov [bp._es],si + mov [bp._di],di + jmp fd_exit ;return ok status + ; + ; AH=15: get drive type + ; +fd_gettyp: mov al,0 ;drive not present + mov byte [m_fdstat],al ;clear status + cmp dl,0 ;drive 0 ? + jnz fd_gett2 + cmp byte [m_fdmed0],0 ;drive present ? + jz fd_gett2 ;:no + mov al,2 ;change line available +fd_gett2: mov [bp._ah],al ;return in AH + jmp fd_exit2 ;don't set CY + ; + ; AH=17: set drive type for format + ; + ; since we only support 1.44 MB, basically a no-op + ; +fd_fmttyp: cmp al,5 + jae fd_fmtt2 + cmp al,0 + jnz fd_dskchg +fd_fmtt2: jmp fd_badcmd + ; + ; AH=16: get disk change status + ; +fd_dskchg: les si,[m_fdbase] ;^disk parameters + mov al,80h ;drive not ready (no drive) + cmp dl,0 ;drive 0 ? + jnz fd_dsk2 + cmp byte [m_fdmed0],0 ;drive present ? + jz fd_dsk2 ;:no + call fd_motor ;turn on motor + call cs_waitbx ;wait for motor startup time + mov byte [m_fdcnt],36 ;restore motor timer + mov dx,fdc_chg ;read disk change line + in al,dx + and al,80h + jz fd_dsk2 ;:not active, AL = 0 + mov al,6 ;disk change active +fd_dsk2: mov [m_fdstat],al + jmp fd_exit1 ;set CY if not 0 + ; + ; AH=18: set media type for format + ; +fd_medtyp: mov al,80h ;drive not ready (no drive) + cmp dl,0 ;drive 0 ? + jnz fd_medt9 + cmp byte [m_fdmed0],0 ;drive present ? + jz fd_medt9 ;:no + mov al,0ch ;invalid format + cmp ch,79 ;80 tracks ? + jnz fd_medt9 ;:no, error + cmp cl,18 ;18 sectors ? + jnz fd_medt9 ;:no, error + mov [bp._es],cs ;return ^disk parameters + mov word [bp._di],fd_ptab + mov al,0 ;ok status +fd_medt9: mov [m_fdstat],al ;set status + jmp fd_exit1 ;set CY if not 0 + ; + ; Initialize floppy drive: detect FDC presence, + ; reset FDC, turn on floppy motor, recalibrate + ; +fd_init: mov dx,fdc_ctrl ;make sure FDC is present - control + mov al,0 ;register should read / write + out dx,al + dec ax + out iowait,al + in al,dx ;read back + cmp al,0ffh + jz fd_init9 ;:doesn't exist + + or byte [m_devflg],1 ;floppy drive present + mov byte [m_fdmed0],17h ;drive A: present + mov ah,0 ;reset drive + int 13h + call fd_motor ;turn on floppy motor + mov al,7 ;recalibrate + call fd_cmd + jb fd_init9 + mov al,0 ;drive 0 + call fd_cmd +fd_init9: ret + ; + ; secondary floppy init + ; +fd_inb: cmp byte [m_fdmed0],0 + jz fd_inb9 ;:no FDC + or byte [m_fdrecal],1 ;set bit - done + mov byte [m_fdtrk0],0 ;set current track + mov byte [m_fdstat],0 ;clear errors + call fd_wsk ;wait for seek complete + jb fd_inb7 ;:error + jmp short fd_inb9 ;ok + + ; error: drive not present + +fd_inb7: mov al,[m_devflg] ;decrement drive count + sub al,40h + jnb fd_inb8 ;there is another "floppy" (e.g. flash) + and al,00111110xb ;no floppy drives present +fd_inb8: mov [m_devflg],al + mov byte [m_fdmed0],0 ;disable drive +fd_inb9: ret diff --git a/FILL16.8 b/FILL16.8 new file mode 100644 index 0000000..8ec8570 --- /dev/null +++ b/FILL16.8 @@ -0,0 +1,7 @@ + ; + ; create 16KB fill pattern + ; + ; (C)2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + + db 04000h DUP 0FFh diff --git a/FILL16.BIN b/FILL16.BIN new file mode 100644 index 0000000..171fa2b --- /dev/null +++ b/FILL16.BIN @@ -0,0 +1 @@ +’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ \ No newline at end of file diff --git a/FILL32.8 b/FILL32.8 new file mode 100644 index 0000000..1c77e05 --- /dev/null +++ b/FILL32.8 @@ -0,0 +1,7 @@ + ; + ; create 32KB fill pattern + ; + ; (C)2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + + db 08000h DUP 0FFh diff --git a/FILL32.BIN b/FILL32.BIN new file mode 100644 index 0000000..d13eba3 --- /dev/null +++ b/FILL32.BIN @@ -0,0 +1 @@ +’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ \ No newline at end of file diff --git a/FILL48.8 b/FILL48.8 new file mode 100644 index 0000000..ae0036b --- /dev/null +++ b/FILL48.8 @@ -0,0 +1,7 @@ + ; + ; create 48KB fill pattern + ; + ; (C)1998-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + + db 0C000h DUP 0FFh diff --git a/FILL48.BIN b/FILL48.BIN new file mode 100644 index 0000000..b07f81c --- /dev/null +++ b/FILL48.BIN @@ -0,0 +1 @@ +’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ \ No newline at end of file diff --git a/HDD.8 b/HDD.8 new file mode 100644 index 0000000..4fd1edf --- /dev/null +++ b/HDD.8 @@ -0,0 +1,1563 @@ + ; + ; Hard Disk BIOS + ; + ; (C)1997-2003 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; Limitations: + ; + ; - HDD must support command EC (identify device). + ; - Read Long, Write Long (hardware-specific number of ECC bytes, + ; rarely used) not supported + ; - Format not supported (not available on IDE drives) + ; - Only AMI / Intel style of CHS translation supported. + ; - Extended disk address mode only works with drives that support + ; LBA. + ; - Because of the way the disk parameter table works (crunched + ; down), new drives are only recognized on a cold start, not + ; on Ctrl-Alt-Del restart. + ; + ; Notes: + ; + ; - Storage of disk parameters requires read/write shadow during POST + ; (can be write protected later). This code will break if shadow + ; is write protected during drive configuration ! + ; + ; pd 050502 - add HDD_LOOSE option + ; pd 040326 - change HDD_NOSLAVE policy, skip slave in any case if + ; no master detected. + ; pd 030720 - add 32 sector mode for small LBA drives + ; pd 030304 - add option HD_INFO -> display config information + ; - change dpt structure to add IDE I/O port and master / + ; slave data + ; - add four drive support, option HD_4DRV + ; pd 001019 - add option HD_EDD -> packet interface + ; pd 001019 - add functions 41 and 48 to support large drives. + ; pd 001017 - fix power saving HLT to avoid race conditions. + ; pd 001017 - don't limit cylinder number to 1023 in hd_lba + ; (ensure correct result for function 15), do limit in + ; function 08. + ; pd 000211 - recognize new SanDisk ID + ; pd 991020 - add hd_top variable, needed to support M-Systems + ; DiskOnChip. + ; pd 990501 - add CDBOOT hook + ; pd 990427 - add ATAPI identify + ; pd 990214 - add hook for IDE speed initialization (cs_ide) + ; pd 990210 - add LBA mode support + ; pd 981010 - fix read handshake + ; pd 980710 - fix function 15: return 0 if drive not present + +#if def DEBUG +HD_DEBUG: ;& comment out for production code +#endif + ; + ; drive parameter structure (stored in data module) + ; +dpt_cyl equ 0 ;number of cylinders +dpt_head equ 2 ;number of heads +dpt_sig equ 3 ;signature, $A0 +dpt_psec equ 4 ;physical sectors per track +dpt_mul equ 5 ;(precompensation) -> multiple count +dpt_shl equ 7 ;(reserved) -> shift count +dpt_ctl equ 8 ;drive control byte +dpt_pcyl equ 9 ;physical cylinders +dpt_phd equ 11 ;physical heads +dpt_port equ 12 ;IDE I/O port (legacy: landing zone) +dpt_sec equ 14 ;logical sectors per track +dpt_dev equ 15 ;master / slave (legacy: reserved) +dpt_len equ 16 ;length of structure + ; + ; disk address packet for extended read/write/verify/seek + ; +#if def HD_EDD +drq_len equ 0 ;packet size in bytes +drq_res equ 1 ;reserved, must be 0 +drq_blk equ 2 ;number of blocks, max. 127 +drq_res2 equ 3 ;reserved, must be 0 +drq_ofs equ 4 ;transfer buffer offset +drq_seg equ 6 ;transfer buffer segment +drq_lba equ 8 ;block number (8 bytes) +#endif + +#if def FLASHDISK +int40: dec dl ;correct floppy drive number + int 40h ;execute floppy interrupt + inc dl ;restore drive number + retf 2 ;return, don't change status +intfld: jmp fldisk + ; + ; INT 13 entry + ; +int13hd: sti + and dl,dl ;flash disk ? + jz intfld + jns int40 ;:floppy + and ah,ah ;reset drive ? + jnz int13hd1 ;:no + cmp dl,byte [cs:hd_top] ;above valid HDD ? + jae int40 ;-> floppy only + int 40h ;reset floppy + mov ah,0 +#else +#if def CDBOOT + + ; redirect to floppy or CD emulation as needed + +int40: test byte [cs:d_cdflag],1 ;emulation enabled ? + jz int40a ;:no + test dl,dl ;drive 0 ? + jnz int40b ;:no + jmp cddisk + +int40a: int 40h ;execute floppy interrupt + retf 2 ;return, don't change status + +int40b: dec dl ;correct floppy drive number + int 40h ;execute floppy interrupt + inc dl ;restore drive number + retf 2 ;return, don't change status +#else + ; execute floppy interrupt + +int40: int 40h ;execute floppy interrupt + retf 2 ;return, don't change status +#endif +#if def ROMDISK +int13fl: jmp fldisk +#endif + ; + ; INT 13 entry + ; +int13hd: sti + and dl,dl ;HDD ? + jns int40 ;:floppy + cmp dl,byte [cs:hd_top] ;compare with max drive number +#if def ROMDISK + jz int13fl +#endif + jae int40 ;:floppy or DiskOnChip + and ah,ah ;reset drive ? + jnz int13hd1 ;:no + cmp dl,byte [cs:hd_top] ;above valid HDD ? + jae int40 ;-> floppy only + int 40h ;reset floppy + mov ah,0 +#endif + ; + ; dispatch disk commands + ; +int13hd1: push ds ;save registers + push es + pusha + mov bp,sp ;access to stack frame + xor di,di ;access BIOS segment + mov ds,di +#if def HD_DEBUG +; test byte [m_kbf],kb_fscrs ;scroll lock ? +; jnz int13dmp1 ;yes: don't display + call v_dump ;dump registers +int13dmp1: +#endif + mov di,ax ;command -> index + shr di,8 + add di,di + and byte [bp+18h],0feh ;clear return carry + cmp di,hd_vec99-hd_vecs ;limit command vector + jae hd_badcmd ;:too high + jmp [cs:di.hd_vecs] ;jump to command + ; + ; Illegal command + ; +hd_badcmd: mov byte [m_hdstat],1 ;illegal command + ; + ; AH=01: get status + ; +hd_status: mov al,[m_hdstat] ;get old status + mov [bp._al],al ;return in AL + ; + ; return status + ; +hd_exit0: mov [m_hdstat],al ;set error code + mov [bp._ah],al ;return in AH +hd_exit1: and al,al ;error ? + jz hd_exit2 ;:no + or byte [bp+18h],1 ;yes: set carry +#if def HD_DEBUG + stc +hd_exit2: pushf + test byte [m_kbf],kb_fscrs ;scroll lock ? + jnz int13dmp2 ;yes: don't display + call v_dump2 ;& dump registers +int13dmp2: popf +#else +hd_exit2: +#endif + popa ;restore registers + pop es + pop ds + iret ;return from interrupt + ; + ; IDE vector table + ; + even +hd_vecs: dw hd_rst ;AH=00: recalibrate drive + dw hd_status ;AH=01: get status + dw hd_read ;AH=02: read + dw hd_write ;AH=03: write + dw hd_verify ;AH=04: verify + dw hd_badcmd ;AH=05: format track -> not supported + dw hd_badcmd ;AH=06: bad + dw hd_badcmd ;AH=07: bad + dw hd_getprm ;AH=08: read drive parameters + dw hd_setprm ;AH=09: set drive parameters + dw hd_badcmd ;AH=0A: read long -> not supported + dw hd_badcmd ;AH=0B: write long -> not supported + dw hd_seek ;AH=0C: seek + dw hd_rst2 ;AH=0D: alternate disk reset (HD only) + dw hd_badcmd ;AH=0E: bad + dw hd_badcmd ;AH=0F: bad + dw hd_trdy ;AH=10: test drive ready + dw hd_recal ;AH=11: recalibrate + dw hd_badcmd ;AH=12: bad + dw hd_badcmd ;AH=13: bad + dw hd_diag ;AH=14: controller diagnostics + dw hd_gettyp ;AH=15: get drive type + dw hd_badcmd ;AH=16: bad + dw hd_badcmd ;AH=17: bad + dw hd_badcmd ;AH=18: bad + dw hd_badcmd ;AH=19: bad + dw hd_badcmd ;AH=1A: bad + dw hd_badcmd ;AH=1B: bad + dw hd_badcmd ;AH=1C: bad + dw hd_badcmd ;AH=1D: bad + dw hd_badcmd ;AH=1E: bad + dw hd_badcmd ;AH=1F: bad + dw hd_badcmd ;AH=20: bad + dw hd_badcmd ;AH=21: bad + dw hd_badcmd ;AH=22: bad +#if def HD_TIME + dw hd_timer ;AH=23: set standby timer NON-STANDARD +#else + dw hd_badcmd +#endif + dw hd_setmul ;AH=24: set multiple mode + dw hd_id ;AH=25: identify drive +#if def HD_EDD + dw hd_badcmd ;AH=26: bad + dw hd_badcmd ;AH=27: bad + dw hd_badcmd ;AH=28: bad + dw hd_badcmd ;AH=29: bad + dw hd_badcmd ;AH=2A: bad + dw hd_badcmd ;AH=2B: bad + dw hd_badcmd ;AH=2C: bad + dw hd_badcmd ;AH=2D: bad + dw hd_badcmd ;AH=2E: bad + dw hd_badcmd ;AH=2F: bad + dw hd_badcmd ;AH=30: bad + dw hd_badcmd ;AH=31: bad + dw hd_badcmd ;AH=32: bad + dw hd_badcmd ;AH=33: bad + dw hd_badcmd ;AH=34: bad + dw hd_badcmd ;AH=35: bad + dw hd_badcmd ;AH=36: bad + dw hd_badcmd ;AH=37: bad + dw hd_badcmd ;AH=38: bad + dw hd_badcmd ;AH=39: bad + dw hd_badcmd ;AH=3a: bad + dw hd_badcmd ;AH=3b: bad + dw hd_badcmd ;AH=3c: bad + dw hd_badcmd ;AH=3d: bad + dw hd_badcmd ;AH=3e: bad + dw hd_badcmd ;AH=3f: bad + dw hd_badcmd ;AH=40: bad + dw hd_edd41 ;AH=41: detect extended interface + dw hd_xrd ;AH=42: extended read + dw hd_xwr ;AH=43: extended write + dw hd_xver ;AH=44: extended verify + dw hd_badcmd ;AH=45: bad (lock / unlock drive) + dw hd_badcmd ;AH=46: bad (eject removable media) + dw hd_xsk ;AH=47: extended seek + dw hd_edd48 ;AH=48: get extended parameters +#endif +hd_vec99: ;end of table + ; + ; AH=00: reset hard disk drives + ; AH=0D: alternate reset (doesn't reset floppy) + ; +hd_rst: +hd_rst2: call hd_inten ;enable HD interrupt + cmp dl,80h + jb hd_rst3 + cmp dl,byte [cs:hd_top] + jb hd_rst4 +hd_rst3: mov dl,80h ;hit primary channel +hd_rst4: call hd_parm ;get table index + + mov dx,word [cs:di.dpt_port] ;port address + add dx,hdc_ctrl + mov al,4 ;soft reset + out dx,al + out iowait,ax ;wait a bit + out iowait,ax + out iowait,ax + out iowait,ax + out iowait,ax + mov al,0 ;end of reset, interrupt enable + out dx,al ;hdc_ctrl + sub dx,hdc_ctrl ;restore hdc base + call hd_busy18 ;wait while busy + jb hd_rst8 ;:error + and dl,0f0h + or dl,hdc_err ;check error status + in al,dx + and al,7fh + sub al,1 + jnz hd_rst8 ;:bad status + mov al,0 ;ok status + jmp hd_exit0 ;return + +hd_rst8: mov al,5 ;reset failed +hd_rst9: jmp hd_exit0 + ; + ; AH=02: read sectors + ; +hd_read: call hd_sel ;select drive + jb hd_read9 + call hd_chs ;translate CHS + jb hd_read9 + mov bl,[bp._al] ;get sector count + cld ;forward mode + mov di,[bp._bx] ;get destination address + mov byte [m_hdflag],0 ;clear interrupt flag + mov al,20h ;issue read command + or dl,hdc_cmd + out dx,al + +hd_read1: call hd_int ;wait for interrupt + jb hd_read9 + or dl,hdc_stat ;read status + in al,dx + mov byte [m_hdflag],0 ;clear interrupt flag for next + test al,1 ;ERR ? + jnz hd_read8 + test al,8 ;DRQ ? + jz hd_read8 ;:no + and dl,0f0h +; or dl,hdc_dat ;read 512 bytes from drive + mov cx,256 + rep insw + dec bl ;another sector ? + jnz hd_read1 ;:yes +hd_read8: sub byte [bp._al],bl ;adjust sector count to reality + call hd_stat ;get status +hd_read9: jmp hd_exit0 + ; + ; AH=03: write sectors + ; +hd_write: mov si,bx ;source address + call hd_sel ;select drive + jb hd_writ9 + call hd_chs ;translate CHS + jb hd_writ9 + mov bl,[bp._al] ;get sector count + cld ;forward mode + mov al,30h ;issue write command + or dl,hdc_cmd + out dx,al + +hd_writ1: or dl,hdc_stat ;read status + xor cx,cx + mov byte [m_hdflag],0 ;clear interrupt flag for next +hd_writ2: in al,dx + test al,8 ;DRQ ? + jnz hd_writ3 ;:yes + test al,21h ;error ? + jnz hd_writ8 + loop hd_writ2 + mov al,80h ;time-out + jmp short hd_writ9 + +hd_writ3: and dl,0f0h + ;or dl,hdc_dat ;write 512 bytes from drive + mov cx,256 + es: rep outsw + call hd_int ;wait for interrupt + jb hd_writ9 + dec bl ;another sector ? + jnz hd_writ1 ;:yes + +hd_writ8: call hd_stat ;get status +hd_writ9: jmp hd_exit0 + ; + ; AH=04: verify sectors + ; +hd_verify: call hd_sel ;select drive + jb hd_ver9 + call hd_chs ;translate CHS + mov al,40h + call hd_cmd ;read verify command + jb hd_ver9 + call hd_stat ;get status +hd_ver9: jmp hd_exit0 + ; + ; AH=08: get drive parameters + ; +hd_getprm: cmp byte [m_hdcnt],0 ;no drives ? + jnz hd_getp1 + jmp hd_badcmd ;:bad command + +hd_getp1: call hd_parm ;get ^parameters + mov al,7 ;invalid drive number + mov cx,0 ;return 0 size + mov dx,0 + jb hd_getp9 ;:not present + + mov dh,[cs:di.dpt_head] ;DH = max head number + dec dh + mov dl,[m_hdcnt] ;DL = number of drives + mov cx,[cs:di.dpt_cyl] ;CX = max cylinders (swapped) + dec cx ;-1 for max cyl +#if def HDD_LBA + cmp byte [cs:di.dpt_shl],0fc ;LBA mode ? + jae hd_getp2 +#endif + dec cx ;deduct one for diagnostic cylinder +hd_getp2: cmp cx,1023 ;limit cylinder to 1023 + jbe hd_getp3 + mov cx,1023 +hd_getp3: xchg cl,ch + shl cl,6 ;CL high 6 bits = cylinders high + or cl,[cs:di.dpt_sec] ;CL = number of sectors + mov al,0 ;clear status + +hd_getp9: mov [bp._cx],cx ;cylinder count + mov [bp._dx],dx ;number drives, heads + jmp hd_exit0 + ; + ; AH=09: set drive parameters + ; +hd_setprm: call hd_sel ;select drive + jb hd_setp9 + mov ah,[cs:di.dpt_phd] ;number heads (physical) + dec ah + and dl,0f0h + or dl,hdc_drv ;set maximum heads + in al,dx + or al,ah + out dx,al + mov al,[cs:di.dpt_psec] ;(physical) + and dl,0f0h + or dl,hdc_cnt ;sector count + out dx,al + mov al,91h ;set drive parameters + call hd_cmd + jb hd_setp9 ;:error + call hd_stat ;check status +hd_setp9: jmp hd_exit0 + ; + ; AH=0C: seek + ; +hd_seek: call hd_sel ;select drive + jb hd_seek9 + call hd_chs ;set CHS value + mov al,70h + call hd_cmd ;seek command + jb hd_seek9 + call hd_stat ;check status +hd_seek9: cmp al,40h ;seek error ? + jnz hd_seek91 + mov al,0 ;don't show it... (Core test will fail +hd_seek91: jmp hd_exit0 ;otherwise) + ; + ; AH=10: test drive ready + ; +hd_trdy: mov cx,0ffffh ;no time-out + call hd_sel0 ;select drive, test ready + jb hd_trdy9 + or dl,hdc_stat ;check status + in al,dx + mov [m_hdst],al + mov ah,0aah ;not ready + test al,40h + jz hd_trdy8 + mov ah,40h ;seek error + test al,10h + jz hd_trdy8 + mov ah,0cch ;write fault + test al,20h + jnz hd_trdy8 + mov ah,0 ;ok status +hd_trdy8: mov al,ah +hd_trdy9: jmp hd_exit0 + ; + ; AH=11: recalibrate + ; +hd_recal: call hd_sel ;select drive + jb hd_rec9 + mov al,10h + call hd_cmd ;recalibrate command + jb hd_rec9 + call hd_stat ;get status +hd_rec9: jmp hd_exit0 + ; + ; AH=14: controller diagnostics + ; +hd_diag: mov dx,hdc ;primary I/O base + call hd_busy18 ;wait for not busy + mov al,20h ;bad controller + jb hd_diag9 ;:bad + mov al,90h ;diagnostic command + or dl,hdc_cmd + out dx,al + out iowait,al + mov cx,18*6 ;max. 6 seconds (!!!) + call hd_busy ;wait for not busy + mov al,80h ;time-out + jb hd_diag9 + and dl,0f0h + or dl,hdc_err ;check error register + in al,dx + and al,7fh + sub al,1 + jz hd_diag9 ;:ok + mov al,20h ;bad controller +hd_diag9: jmp hd_exit0 + ; + ; AH=15: read DASD type + ; +hd_gettyp: call hd_parm ;get pointer to parameter block + jb hd_gett8 ;:not present + mov al,[cs:di.dpt_head] ;number heads + mul byte [cs:di.dpt_sec] ;number sectors + mov dx,[cs:di.dpt_cyl] ;number cylinders +#if def HDD_LBA + cmp byte [cs:di.dpt_shl],0fc ;LBA mode ? + jae hd_gett2 +#endif + dec dx ;minus one for diagnostics +hd_gett2: mul dx + mov cl,3 ;drive present + jmp short hd_gett9 + +hd_gett8: xor ax,ax ;0 = drive not present + xor cx,cx + xor dx,dx +hd_gett9: mov byte [bp._ah],cl ;0 = not present, 3 = present + mov [bp._cx],dx ;CX = MSB sector count + mov [bp._dx],ax ;DX = LSB sector count + mov al,0 ;ok status + mov [m_hdstat],al + jmp hd_exit1 + ; + ; AH = 24: set multiple mode + ; +hd_setmul: call hd_sel ;select drive + jb hd_setm9 + and dl,0f0h + or dl,hdc_cnt + mov al,[bp._al] ;number of sectors + out dx,al + mov al,0c6h + call hd_cmd ;set multiple mode command + jb hd_setm9 + call hd_stat ;get status +hd_setm9: jmp hd_exit0 + ; + ; AH=25: identify drive + ; +hd_id: call hd_selb ;select drive + ;ignore time-out here, if drive not ready + ;(ATAPI drive doesn't report ready + ;until spoken to) + mov al,0ech ;identify drive + call hd_cmd ;issue command + jb hd_id9 ;:bad drive + in al,dx ;hdc_stat + test al,1 ;error ? + jz hd_id1 ;:no + +hd_id0: mov al,0a1h ;ATAPI identify drive + call hd_cmd ;issue command + jb hd_id9 ;:time-out + +hd_id1: xor cx,cx +hd_id2: in al,dx ;hdc_stat + test al,8 ;DRQ ? + jnz hd_id3 ;:yes + loop hd_id2 + mov al,80h ;time-out + jmp short hd_id9 + +hd_id3: cld ;forward direction + and dl,0f0h + ;or dl,hdc_dat + mov cx,256 ;512 bytes + mov di,bx ;destination + rep insw ;read data + call hd_stat ;get status +hd_id9: jmp hd_exit0 ;exit + +#if def HD_EDD + ; + ; AH=41: detect EDD support + ; +hd_edd41: cmp bx,55aah ;magic cookie ? + jnz hd_edd419 ;no: bad + mov word [bp._bx],0aa55h ;return cookie + mov word [bp._cx],1 ;support packet commands; no lock / + ;eject + mov byte [bp._ah],1 ;major version + mov byte [m_hdstat],0 + jmp hd_exit2 ;return carry clear + +hd_edd419: jmp hd_badcmd ;return error + ; + ; AH=42: extended read + ; +hd_xrd: call hd_sel ;select drive + jb hd_xrd9 + call hd_xadr ;handle address + jb hd_xrd9 + mov byte [m_hdflag],0 ;clear interrupt flag + mov al,20h ;issue read command + or dl,hdc_cmd + out dx,al + +hd_xrd1: call hd_int ;wait for interrupt + jb hd_xrd9 + or dl,hdc_stat ;read status + in al,dx + mov byte [m_hdflag],0 ;clear interrupt flag for next + test al,1 ;ERR ? + jnz hd_xrd8 + test al,8 ;DRQ ? + jz hd_xrd8 ;:no + and dl,0f0h + ;or dl,hdc_dat ;read 512 bytes from drive + mov cx,256 + rep insw + dec bl ;another sector ? + jnz hd_xrd1 ;:yes +hd_xrd8: mov es,[bp._ds] ;access address packet + sub byte [es:si+drq_blk],bl ;adjust sector count to reality + call hd_stat ;get status +hd_xrd9: jmp hd_exit0 + ; + ; AH=43: extended write + ; +hd_xwr: call hd_sel ;select drive + jb hd_xwr9 + call hd_xadr ;handle address + jb hd_xwr9 + mov si,di ;buffer ^ + mov al,30h ;issue write command + or dl,hdc_cmd + out dx,al + +hd_xwr1: or dl,hdc_stat ;read status + xor cx,cx + mov byte [m_hdflag],0 ;clear interrupt flag for next +hd_xwr2: in al,dx + test al,8 ;DRQ ? + jnz hd_xwr3 ;:yes + test al,21h ;error ? + jnz hd_xwr8 + loop hd_xwr2 + mov al,80h ;time-out + jmp short hd_xwr9 + +hd_xwr3: and dl,0f0h + ;or dl,hdc_dat ;write 512 bytes from drive + mov cx,256 + es: rep outsw + call hd_int ;wait for interrupt + jb hd_xwr9 + dec bl ;another sector ? + jnz hd_xwr1 ;:yes + +hd_xwr8: call hd_stat ;get status +hd_xwr9: mov es,[bp._ds] ;access address packet + mov si,[bp._si] + sub byte [es:si+drq_blk],bl ;adjust sector count to reality + jmp hd_exit0 + ; + ; AH=44: extended verify + ; +hd_xver: call hd_sel ;select drive + jb hd_xver9 + call hd_xadr ;handle address + jb hd_xver9 + mov al,40h + call hd_cmd ;read verify command + jb hd_xver9 + call hd_stat ;get status +hd_xver9: jmp hd_exit0 + ; + ; AH=47: extended seek + ; +hd_xsk: call hd_sel ;select drive + jb hd_xsk9 + call hd_xadr ;handle address + jb hd_xsk9 + mov al,70h + call hd_cmd ;seek command + jb hd_xsk9 + call hd_stat ;check status +hd_xsk9: jmp hd_exit0 + ; + ; AH=48: return drive parameters + ; + ; Note: Phoenix spec says we should return PHYSICAL geometry, but + ; Award BIOS returns LOGICAL... Users of this function are most + ; interested in the max sector count anyway. + ; +hd_edd48: call hd_parm ;get ^parameter block -> DI + jb hd_edd489 + mov si,di ;^parameter block + cld ;forward direction + mov es,[bp._ds] ;buffer segment + mov di,[bp._si] ;buffer offset + mov al,1 ;(error code) + cmp word [es:di],26 ;buffer at least 26 bytes long + jb hd_edd489 ;less -> error + mov ax,26 ;buffer length + stosw + mov ax,2 ;flags: valid geometry + stosw + xor eax,eax + mov ax,[cs:si.dpt_cyl] ;number of cylinders + stosd + mov al,[cs:si.dpt_head] ;number of heads + mov ah,0 + stosd + mov al,[cs:si.dpt_sec] ;number of sectors +; mov ah,0 + stosd + mov al,[cs:si.dpt_head] ;number heads + mul byte [cs:si.dpt_sec] ;number sectors + mov dx,[cs:si.dpt_cyl] ;number cylinders + mul dx + stosw ;-> physical sector count + xchg ax,dx + stosw + xor eax,eax + stosd + mov ax,512 ;bytes per sector + stosw + mov al,0 ;ok status +hd_edd489: jmp hd_exit0 + ; + ; write LBA address to command file + ; + ; returns sector count in BL, transfer address in ES:DI + ; + ; this will break on old drives that don't support LBA + ; +hd_xadr: mov es,[bp._ds] ;restore segment, SI still OK + cmp byte [es:si+drq_len],16 ;at least 16 bytes + jb hd_xadr9 ;:error + and dl,0f0h + or dl,hdc_cnt ;sector count + mov al,[es:si+drq_blk] + mov bl,al ;return in BL + out dx,al + inc dx + mov eax,[es:si+drq_lba] ;LBA sector number + out dx,al ;hdc_sec sector = LBA 7..0 + inc dx + shr ax,8 + out dx,al ;hdc_cyl cylinder low = LBA 15..8 + inc dx + shr eax,16 + out dx,al ;hdc_cyh cylinder high = LBA 23..16 + inc dx + in al,dx ;hdc_drv get drive + and al,0b0h ;keep reserved, drive select bits + or al,40h ;set LBA mode + or al,ah + out dx,al ;hdc_drv heads = LBA27..24 + mov di,[es:si+drq_ofs] ;get ^transfer buffer + mov es,[es:si+drq_seg] + cld ;forward mode + clc ;ok + ret + +hd_xadr9: mov al,1 ;return error + stc + ret +#endif + ; + ; wait for not busy, check status + ; +hd_stat0: call hd_busy18 ;wait until not busy + jb hd_stat9 + + ; Enter here for faster service (assuming normally not busy) + ; This is arranged to get fastest response when no error. + +hd_stat: or dl,hdc_stat ;test whether busy + in al,dx + test al,80h + jnz hd_stat0 ;:busy + mov [m_hdst],al + mov ah,al ;save status + test al,24h ;write fault / ECC ? + jnz hd_stat1 + and al,50h ;not ready, or seek error ? + cmp al,50h + jnz hd_stat2 + test ah,1 ;other error ? + jnz hd_stat3 + mov al,0 ;return ok status + ret + +hd_stat1: mov al,11h ;ECC corrected data + test ah,4 + jnz hd_stat9 + mov al,0cch ;no - must be write fault +hd_stat9: stc + ret + +hd_stat2: mov al,0aah ;not ready + test ah,40h + jz hd_stat9 + mov al,40h ;no - must be seek error + stc + ret + +hd_stat3: and dl,0f0h + or dl,hdc_err ;read error register + in al,dx + mov [m_hderr],al + mov si,hd_errtab + cmp al,0 ;nothing set -> undefined error + jz hd_stat5 +hd_stat4: inc si + shl al,1 + jnb hd_stat4 +hd_stat5: mov al,[cs:si] ;get error code + stc + ret + ; + ; error register -> error code translation + ; +hd_errtab: db 0e0h ;nothing set - status error + db 0ah ;80 - bad sector flag detected + db 10h ;40 - bad ECC + db 0bbh ;20 - undefined error + db 04h ;10 - record not found + db 01h ;08 - abort -> bad command + db 0bbh ;04 - undefined error + db 40h ;02 - seek error + db 02h ;01 - address mark not found + ; + ; get pointer to parameter block + ; + ; entry: DL = drive + ; exit: CS:DI = parameter block + ; +hd_parm: cmp dl,byte [cs:hd_top] ;valid drive number ? + jae hd_parm9 ;:bad + mov di,dx ;drive number + and di,7fh + shl di,4 ;* 16 dpt_len + add di,offset hd_prm0 ;+ table base + ;clc + ret + +hd_parm9: mov al,1 ;error code + stc + ret + ; + ; wait while HD busy, CX ticks, DX port base + ; +hd_busy18: mov cx,18 ;18 ticks = 1 second +hd_busy: add cx,[m_timer] ;start time + max number of ticks + and dl,0f0h + or dl,hdc_stat +hd_busy1: in al,dx + test al,80h ;busy ? + jz hd_busy9 ;:no, carry clear + cmp cx,[m_timer] + jns hd_busy1 ;keep waiting +hd_busy8: stc ;time-out + mov al,80h ;status code +hd_busy9: ret + ; + ; select drive, wait for drive ready + ; + ; -> CS:DI = ^parameter block + ; + + ; special entry: no drive number check + +hd_selb: mov cx,18 ;1 s time-out + and dl,7fh ;mask drive number + jmp short hd_sel0a + + ; normal entry + +hd_sel: mov cx,18 ;1 s time-out for ready wait +hd_sel0: and dl,7fh ;legal drive number ? + cmp dl,[m_hdcnt] + jae hd_parm9 ;:bad +hd_sel0a: mov di,dx + and di,007fh ;mask drive number + shl di,4 + add di,offset hd_prm0 + mov byte [m_hdflag],0 ;clear interrupt flag + mov al,byte [cs:di.dpt_dev] + or al,0a0h + mov dx,word [cs:di.dpt_port] + or dl,hdc_drv + out dx,al ;set drive + ; + ; wait until HD ready, CX ticks + ; +hd_rdy: add cx,[m_timer] ;start time + max number of ticks + or dl,hdc_stat +hd_rdy1: in al,dx + test al,80h + jnz hd_rdy2 ;:busy + test al,40h + jnz hd_busy9 ;:ready, carry clear +hd_rdy2: cmp cx,[m_timer] + jns hd_rdy1 ;keep waiting + jmp hd_busy8 + ; + ; issue command AL, wait for interrupt + ; +hd_cmd: mov byte [m_hdflag],0 ;clear interrupt flag + or dl,hdc_cmd + out dx,al + + ; wait for HD interrupt + +hd_int: mov cx,18*4 ;4 seconds + add cx,[m_timer] ;start time + max number of ticks +hd_int1: cli ;test in critical section as some + ;modern drives are "too fast" for + ;slower embedded boards. + test byte m_hdflag,0ffh ;interrupt ? + jnz hd_int9 ;:yes, return NC + cmp cx,[m_timer] ;time-out ? + js hd_int8 ;:yes, return CY + sti ;end critical section, HLT follows + hlt ;power-saving wait for next interrupt + jmp hd_int1 + +hd_int8: stc ;time-out + mov al,80h ;status code +hd_int9: sti ;re-enable interrupts ! + ret + ; + ; IRQ14 entry + ; +#if ! def irq14 ;(optional user override) +irq14: +irq15: push ax + push ds + xor ax,ax ;BIOS segment + mov ds,ax + mov byte [m_hdflag],0ffh ;set interrupt flag + mov al,eoi + out pic1,al + out pic0,al + pop ds + pop ax + iret +#endif + ; + ; set hard disk time-out + ; +#if def HD_TIME +hd_timer: + call hd_sel ;select drive, wait for not busy + jb hd_tim9 + and dl,0f0h + or dl,hdc_cnt + mov al,HD_TIME + out dx,al + mov al,0e3h + call hd_cmd ;issue command + jb hd_tim9 + and dl,0f0h + or dl,hdc_err + in al,dx + and al,7fh + jz hd_tim9 + sub al,1 ;ok ? + jz hd_tim9 + mov al,20h + stc +hd_tim9: jmp hd_exit0 +#endif + ; + ; write CHS parameters to command file, including CHS translation + ; +hd_chs: and dl,0f0h + or dl,hdc_cnt ;sector count + mov al,[bp._al] + out dx,al + inc dx + mov cl,[cs:di.dpt_shl] ;get shift count +#if def HDD_LBA + cmp cl,0fch ;LBA mode ? + jae hd_chs2 +#endif + mov bx,[bp._cx] ;sector number, cylinder + mov al,bl ;sector number + and ax,3fh ;(need AH = 0 for divide) + out dx,al ;hdc_sec + mov al,[bp._dh] ;head number + div byte [cs:di.dpt_phd] ;divide by physical heads + ;-> AL = heads, AH = cylinders + inc dx + xchg bl,bh ;swap cylinder + shr bh,6 ;bit 7..6 become bits 9..8 + shl bx,cl ;shift cylinder for CHS translation + or al,bl ;head + out dx,al ;hdc_cyl - cylinder low + inc dx + mov al,bh ;cylinder high + out dx,al ;hdc_cyh + inc dx + in al,dx ;hdc_drv + or al,ah ;heads + out dx,al + ret + ; + ; LBA translation + ; +#if def HDD_LBA +hd_chs2: push eax ;save eax, ebx + push ebx + xor eax,eax + mov ax,[bp._cx] ;sector number, cylinder + xchg al,ah ;swap cylinder high, low + shr ah,6 ;fix cylinder high + cmp cl,0ff ;FF = 255 heads + jnz hd_chs3 + mov ebx,eax + shl eax,8 ;cylinder * 255 + sub eax,ebx + jmp hd_chs4 + +hd_chs3: shl eax,5 ;* 32 + cmp cl,0fc ;FC = 32 heads + jz hd_chs4 + + shl eax,1 ;* 64 + cmp cl,0fd ;FD = 64 heads + jz hd_chs4 + + shl eax,1 ;* 128 + +hd_chs4: xor ebx,ebx + mov bl,[bp._dh] ;head number + add eax,ebx ;add head + mov ebx,eax + shl eax,6 ;cylinder * 255 + head * 63 + sub eax,ebx + xor ebx,ebx + mov bl,[bp._cl] ;sector number + and bl,63 + dec bx ;- 1 + add eax,ebx + out dx,al ;hdc_sec sector = LBA 7..0 + inc dx + shr ax,8 + out dx,al ;hdc_cyl cylinder low = LBA 15..8 + inc dx + shr eax,16 + out dx,al ;hdc_cyh cylinder high = LBA 23..16 + inc dx + in al,dx ;hdc_drv get drive + and al,0b0h ;keep reserved, drive select bits + or al,040h ;set LBA mode + or al,ah + out dx,al ;hdc_drv heads = LBA27..24 + pop ebx + pop eax + ret +#endif + ; + ; enable HD interrupt + ; +hd_inten: cli + in al,pic1+1 ;enable HD interrupt +#if def HD_4DRV + and al,03fh +#else + and al,0bfh +#endif + out iowait,ax + out pic1+1,al + + in al,pic0+1 ;enable cascade interrupt + and al,0fbh + out iowait,ax + out pic0+1,al + sti + ret + ; + ; HD detect / init + ; +hd_init: + +#if def HD_WAIT + ; + ; Some drives take a long time to become responsive to commands, + ; because they only store very minimal firmware, and fetch the + ; actual code from disk. Some of them are allergic to being touched + ; before they are ready. + ; + cmp word [m_rstflg],1234h ;Ctrl-Alt-Del ? + jz hd_wait9 ;:skip wait - drives should be ready + xor bx,bx ;clear second counter + mov si,msg_wait + call v_msg + cmp bx,HD_ENA ;0 delay ? + jz hd_wait3 ;yes: bypass + +hd_wait1: mov ax,18 ;about 1 second + add ax,[m_timer] ;start time + max number of ticks +hd_wait2: hlt ;low power wait, we'll be here for a + ;while + cmp ax,[m_timer] ;time-out ? + jns hd_wait2 ;no: keep waiting + + cmp bx,HD_ENA ;can we touch the drive now ? + jb hd_wait8 ;:no +hd_wait3: mov al,0ffh ;place FF on the IDE bus (or loopback) + mov dx,hdc+hdc_dat + out dx,al + or dl,hdc_stat ;does the status register read non-FF ? + in al,dx + cmp al,0ffh + jz hd_wait8a ;FF: no drive attached, bail + test al,80h ;busy ? + jnz hd_wait8 ;:don't touch + mov al,0a0h ;access master drive + and dl,0f0h + or dl,hdc_drv + out dx,al + out iowait,ax + or dl,hdc_stat ;read status + in al,dx + test al,80h ;busy ? + jnz hd_wait8 + test al,40h ;drive ready ? + jnz hd_wait8a ;:terminate the wait + +hd_wait8: mov si,msg_dot ;display a dot each second + call v_msg + inc bx ;second counter + cmp bx,HD_WAIT + jb hd_wait1 + +hd_wait8a: mov si,msg_crlf ;go to next line + call v_msg +hd_wait9: +#endif + + cli + mov ax,int13hd ;set interrupt vector + xchg [vec13],ax + mov [vec40],ax + mov ax,cs ;old INT13 becomes INT40 + xchg [vec13+2],ax + mov [vec40+2],ax + + mov word [vec41],hd_prm0 ;set vectors to disk parameters + mov [vec41+2],cs + mov word [vec46],hd_prm1 + mov [vec46+2],cs + + call hd_inten ;enable HD interrupt + mov al,byte [cs:hd_top] ;set number of drives to start + and al,7fh + mov [m_hdcnt],al + mov byte [m_hdstat],0 ;clear status + + ; Unfortunately, it is not that easy to detect the slave drive, + ; as the master drive will often drive the slave registers to + ; "safe" values when the slave is not present. + ; + ; It is supposed to be possible to detect number of drives with + ; the execute drive diagnostic command, but I don't see how. + ; + ; In the end, if the detection was incorrect, we will time out + ; (about a second) when trying to identify the drive. + ; + ; To save time, skip slave detection if no master was seen. + + mov di,hd_prm0-dpt_len ;setup first drive + mov dl,80h-1 + +hd_init1: inc dl ;next drive + add di,dpt_len + cmp dl,byte [cs:hd_top] ;done ? + jz hd_init2 ;:yes +#if def HDD_NOSLAVE ;time-saver: skip slave drives + test byte [cs:di.dpt_dev],10h ;slave ? + jnz hd_init1 ;yes: skip +#else + test byte [cs:di.dpt_dev],10h ;slave ? + jz hd_init1a ;:no + shr byte [cs:hd_good],1 ;was master good ? + jnc hd_init1 ;no: skip +hd_init1a: +#endif + +#if ! def HDD_PRES + mov byte [cs:hd_good],0 ;clear good flag + call hd_pres ;check presence + jb hd_init1 ;:not present + inc byte [cs:hd_good] ;set good flag +#endif + call hd_set ;set parameters + ;jb hd_init1 ;(if bad, we will find out later) + jmp hd_init1 + +hd_init2: mov di,hd_prm0 ;count drives, crunch descriptors + mov dl,0 ;clear drive count + mov dh,byte [cs:hd_top] ;current drive count + sub dh,07fh + push ds ;save DS, ES + push es + push cs ;DS = CS, ES = CS + pop ds + push cs + pop es + lea si,[di.-dpt_len] +hd_init3: add si,dpt_len ;go to next descriptor +hd_init4: dec dh ;another drive ? + jbe hd_init5 ;:no + test word [si.dpt_cyl],0ffffh ;0 cylinders ? + jz hd_init3 ;:drive doesn't exist + mov cx,dpt_len / 2 ;copy descriptor down + rep movsw + inc dl ;count good drives + jmp hd_init4 + +hd_init5: mov cx,offset hd_prm99 ;fill remaining descriptors + sub cx,di + jz hd_init6 + shr cx,1 + xor ax,ax + rep stosw +hd_init6: pop es + pop ds + mov byte [m_hdcnt],dl ;store number of drives + or dl,80h + mov byte [cs:hd_top],dl ;store top hard disk number +#if def HD_INFO + jmp hd_cr +#else + ret +#endif + ; + ; check drive presence, DI = ^device block + ; +hd_pres: push dx + mov dx,word [cs:di.dpt_port] + or dl,hdc_drv + mov al,byte [cs:di.dpt_dev] + or al,0a0h + out dx,al + out iowait,ax + out iowait,ax + out iowait,ax + + and dl,0f0h + or dl,hdc_cnt ;write test pattern + mov al,55h + out dx,al + and dl,0f0h + or dl,hdc_cyl ;write negative pattern + mov al,0aah + out dx,al + and dl,0f0h + or dl,hdc_cnt ;read test pattern + in al,dx + xor al,55h + jz hd_pres9 ;:ok +hd_pres8: +#if def HD_INFO + pusha + call hd_cr + mov ax,dx ;display port address + and al,0f0 ;(change into base) + call hex + mov si,offset hd_nf ;not found + call v_msg + popa +#endif + stc +hd_pres9: pop dx + ret + ; + ; set up drive DL, drive parameters DI + ; +hd_set: mov ah,25h ;get drive ID + mov bx,tmp_buf + int 13h + jb hd_set9 + +#if def HD_INFO + call hd_inf1 ;display drive info, part 1 +#endif + +#if def cs_ide + push di ;save ^drive parameters + mov di,tmp_buf ;DS:DI points to identify buffer + push dx ;DL: drive + call cs_ide ;set drive timing parameters + pop dx + pop di +#endif + +#if def HDD_LOOSE + mov ax,[tmp_buf+0] ;device ID + and ax,084c0 + cmp ax,08480 ;removable CF + jz hd_set0 + cmp ax,08440 ;not removable CF + jz hd_set0 + test ah,080 ;ATAPI ? + jz hd_set0 +#else + cmp word [tmp_buf+0],848ah ;CompactFlash ? + jz hd_set0 ;:yes + cmp word [tmp_buf+0],844ah ;CompactFlash ? (new SanDisk) + jz hd_set0 ;:yes + test byte [tmp_buf+1],80h ;ATAPI ? + jz hd_set0 ;:no +#endif + + ; note I/O base and drive ID of ATAPI CD-ROM + ; this is assumed to be the first ATAPI device found + +#if def CDBOOT + cmp byte [cs:d_cdbase],0 ;is this the first ATAPI drive ? + jnz hd_set9 ;:no + mov word [cs:d_cdbase],01f0h ;set address + test dl,1 ;master ? + jnz hd_set9 ;:slave, default + mov byte [cs:d_cddrv],0a0h ;master drive +#endif + +#if def HD_INFO + call hd_inf2 ;say ATAPI +#endif + +hd_set9: stc ;error return + ret + +hd_set0: mov al,[tmp_buf+12] ;sectors + mov [cs:di.dpt_sec],al + mov [cs:di.dpt_psec],al + mov al,[tmp_buf+94] ;multiple block size + mov [cs:di.dpt_mul],al + mov ax,[tmp_buf+2] ;cylinders + mov [cs:di.dpt_pcyl],ax + mov bl,[tmp_buf+6] ;heads + mov [cs:di.dpt_phd],bl + + ; CHS translation: shift cylinders right / heads left until + ; cylinders < 1024 + + mov bh,0 ;shift count + +#if def cfg_lba + cmp word [cs:cfg_ofs+cfg_lba],0 + jz hd_set1 ;0 -> stay in CHS mode + cmp ax,[cs:cfg_ofs+cfg_lba] + jae hd_lba ;select LBA if cylinder count >= config +#else +#if def FORCE_LBA + cmp ax,FORCE_LBA ;force LBA for high cylinder count + ja hd_lba +#endif +#endif + +hd_set1: cmp ax,1024 + jb hd_set2 + shr ax,1 ;cylinders / 2 + shl bl,1 ;heads * 2 +#if def HDD_LBA + jb hd_lba ;:overflow - use LBA mode for this drive +#else + jb hd_set9 ;:overflow - cannot translate drive +#endif + inc bh ;count the shifts + jmp hd_set1 + +hd_set2: mov [cs:di.dpt_cyl],ax + mov [cs:di.dpt_head],bl + mov [cs:di.dpt_shl],bh + + mov byte [cs:di.dpt_sig],0a0h ;signature + + mov ah,9 ;set drive parameters + int 13h + jb hd_set9 + + mov ah,0dh ;reset drive + int 13h + +hd_set2b: + +#if def HD_INFO + call hd_inf3 ;display physical / logical CHS +#endif + + push dx + mov ah,8 ;get max CHS + int 13h + mov al,dh ;heads + pop dx + jb hd_set9 + + mov ah,4 ;verify sectors + mov dh,al ;max head + mov al,cl ;max sector -> sector count + and al,3fh + and cl,0c0h ;start sector = 1 + or cl,1 + sub ch,1 ;cylinder - 1 + jnb hd_set3 + sub cl,40h +hd_set3: int 13h + jb hd_set9 + +#if def HD_TIME + mov ah,23h ;set drive time-out + mov al,HD_TIME + int 13h +#endif + ret ;normal return + +#if def HDD_LBA + ; + ; determine LBA parameters + ; +hd_lba: test byte [tmp_buf+99],2 ;LBA mode supported ? + jz hd_set9 ;:no + + push dx + + mov eax,dword [tmp_buf+120] ;number of LBA sectors (low) + mov dx,[tmp_buf+122] ;(high) + mov bx,32*63 + mov cx,0fc00+32 ;32 heads + cmp eax,1023*32*63 ;max size ? + jbe hd_lba2 + + mov bx,64*63 ;64 heads + mov cx,0fd00+64 + cmp eax,1023*64*63 ;max size ? + jbe hd_lba2 + + mov bx,128*63 ;128 heads + mov cx,0fe00+128 + cmp eax,1023*128*63 + jb hd_lba2 + + mov bx,255*63 ;255 heads + mov cx,0ff00+255 + +hd_lba2: div bx ;/ heads / sectors + + ; set drive parameters + + mov [cs:di.dpt_cyl],ax + mov byte [cs:di.dpt_head],cl ;number of heads + mov byte [cs:di.dpt_shl],ch; special shift -> LBA mode + mov byte [cs:di.dpt_sec],63 ;63 sectors + pop dx + jmp hd_set2b ;note we don't set LBA parameters +#endif + ; + ; display drive information + ; +#if def HD_INFO + ; + ; display drive info (DI = ^drive parms, tmp_buf = identify blk) + ; +hd_inf1: pusha + call hd_cr ;display CR/LF + mov ax,[cs:di.dpt_port] ;display port + call hex + mov si,offset hd_mstr ;master + test byte [cs:di.dpt_dev],10h ;slave ? + jz hd_inf1a + mov si,offset hd_slve ;slave +hd_inf1a: call v_msg + mov ax,[tmp_buf+0] ;display config bits + call hex + mov si,offset tmp_buf+27*2 + mov cx,20 +hd_inf1b: lodsw ;display drive model number + push ax + mov al,ah ;big endian format ! + call putc + pop ax + call putc + loop hd_inf1b + popa + ret + ; + ; say this is an ATAPI device + ; +hd_inf2: pusha + mov si,offset hd_atapi + call v_msg + popa + ret + ; + ; display physical and logical information + ; +hd_inf3: pusha + mov si,offset hd_phys + call v_msg + movzx eax,word [tmp_buf+2] ;"phys" cylinders + call hd_ints + movzx eax,word [tmp_buf+6] ;"phys" heads + call hd_ints + movzx eax,word [tmp_buf+12] ;"phys" sectors + call post_itoa + + mov si,offset hd_log + call v_msg + movzx eax,word [cs:di.dpt_cyl] ;logical cylinders + call hd_ints + movzx eax,byte [cs:di.dpt_head] ;logical heads + call hd_ints + movzx eax,byte [cs:di.dpt_sec] ;logical sectors + call post_itoa + + cmp byte [cs:di.dpt_shl],0ffh ;LBA mode ? + jnz hd_inf3a + mov si,offset hd_vlba ;say so + call v_msg +hd_inf3a: popa + ret + +hd_cr: mov si,offset msg_crlf + jmp v_msg + +hd_ints: call post_itoa ;display number + mov al,"/" ;display trailing / + jmp putc + +hd_mstr: db "Master ",0 +hd_slve: db "Slave ",0 +hd_atapi: db "ATAPI",0 +hd_phys: db 13,10,"Phys C/H/S ",0 +hd_log: db " Log C/H/S ",0 +hd_vlba: db " LBA",0 +hd_nf: db "- no drive found ! ",0 + +#endif diff --git a/INT1X.8 b/INT1X.8 new file mode 100644 index 0000000..193583a --- /dev/null +++ b/INT1X.8 @@ -0,0 +1,398 @@ + ; + ; Miscellaneous interrupts + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; Limitations: + ; + ; - INT15 support is very limited. + ; + ; pd 050206 add Int15 E820, change to d_exmem + ; pd 020215 add NO_RTC option + ; pd 991127 add PS/2 mouse hook + + ; + ; Dummy interrupts -> IRET + ; +intdummy: +int00: ;divide by zero +int01: ;single step +int03: ;breakpoint +int04: ;overflow +int06: ;invalid opcode +int07: ;coprocessor not available +int1b: ;keyboard break +int1c: ;user timer tick + + iret + + ; + ; NMI + ; +nmi: push ax + in al,port61 ;check type of NMI +#if ! def NO_NMI + shl al,1 + jb nmi1 ;$80 set: parity error + shl al,1 + jb nmi2 ;$40 set: I/O check +#else + mov al,33h ;display POST code on NMI + out post,al +#endif + mov al,0dh ;read CMOS register -> clear NMI + out cm_idx,al + out iowait,al + in al,cm_dat + pop ax + iret + +#if ! def NO_NMI + + ; display error message + +nmi1: mov si,offset msg_parit + jmp short nmi3 + +nmi2: mov si,offset msg_iochk + +nmi3: mov ax,7 ;set video mode + int 10h + call v_msg ;display message + mov si,offset msg_halt + call v_msg ;"system halted" + cli ;hang system + hlt +#endif + ; + ; end of interrupt, primary controller + ; +inteoi: push ax + mov al,eoi + out pic0,al + pop ax + iret + ; + ; end of interrupt, secondary controller + ; +inteoi2: push ax + mov al,eoi + out pic1,al + out pic0,al + pop ax + iret + ; + ; INT 5: print screen + ; +int05: push ax + push ds + xor ax,ax + mov ds,ax + mov byte [m_prtsc],0ffh ;error + pop ds + pop ax + iret + ; + ; INT 11 entry: equipment flag + ; +int11: push ds + xor ax,ax + mov ds,ax + mov ax,[m_devflg] + pop ds + iret + ; + ; INT12 entry: memory size + ; +int12: push ds + xor ax,ax + mov ds,ax + mov ax,[m_lomem] + pop ds + iret + ; + ; AH=87: block move + ; + ; This assumes that A20 gate is always open (preferably using + ; port 92), and uses "unreal mode". Interrupts are disabled during + ; block move, which can add considerably to interrupt latency. + ; + ; Note we don't close the A20 gate on return. + ; +int1587: push ax + push bx + push si + push di + push ds + push es + call cs_a20on ;enable A20 gate + cld + and ecx,0000ffffh + mov edi,[es:si+1ah] ;24 bit destination address + and edi,00ffffffh ;mask high bits + mov esi,[es:si+12h] ;24 bit destination address + and esi,00ffffffh ;mask high bits + + ; enter unreal mode + + cli ;disable interrupts + lgdt [cs:gdt] ;load GDT + mov eax,cr0 + or al,1 ;enable protected mode + mov cr0,eax + jmp short int15872 ;flush queue +int15872: mov bx,8 ;selector + mov ds,bx + mov es,bx + and al,0feh ;exit protected mode + mov cr0,eax + + shr cx,1 ;convert to 32 bit words + a4 rep movsd ;do the block move + jnb int15873 + a4 movsw ;move a 16 bit "orphan" +int15873: pop es + pop ds + sti ;now interrupts are ok again + pop di + pop si + pop bx + pop ax + mov ah,0 ;ok return + clc + retf 2 + ; + ; INT15 function 86: wait + ; +int1586: pusha + mov bx,dx ;microseconds / 1024 -> BX + shr bx,10 + shl cx,6 + or bx,cx + call cs_waitbx ;do the delay + popa + clc ;return ok status + retf 2 + ; + ; INT15 entry: multiplex interrupt + ; +int15: sti + +;& push ax ;display command code +;& mov al,ah +;& call diaghex +;& pop ax + + cmp ah,87h ;block move ? + jz int1587 +#if def PS2MOUSE + cmp ah,0c2h ;PS/2 mouse ? + jnz int15nc2 + jmp int15c2 +int15nc2: +#endif + cmp ah,88h ;memory size determine ? + jz int1588 + cmp ah,0c0h ;configuration table ? + jz int15c0 + cmp ah,86h ;wait ? + jz int1586 + cmp ax,0e801h ;big memory size ? + jz int15e8 + cmp ax,0e820h ;system memory map ? + jz inte820 + +#if def INT15_24 + cmp ax,02400 + jz int1524 + cmp ax,02401 + jz int1524 +#endif + +#if def CLE266 + cmp ah,05f ;VIA VGA API ? + jnz int15ex ;:yes + jmp int15_cle +#endif + +int15ex: mov ah,86h ;bad command + stc + retf 2 ;return + +#if def INT15_24 + + ; Function 24xx is supposed to control the A20 gate. + ; This is added to keep GRUB from using the keyboard + ; controller on legacy free systems. + +int1524: mov ah,00 ;A20 gate "support" - we just lie + clc ;and say we did it... tinyBIOS always + retf 2 ;leaves A20 gate open +#endif + ; + ; AH=88: determine extended memory size + ; +int1588: +#if def NO_RTC + mov eax,dword [cs:d_exmem] ;get extended memory size + shr eax,10 ;convert to KB + cmp eax,63*1024 ;limit to 64MB + jb >l1 + mov eax,63*1024 +l1: +#else + push bx + mov ah,cm_exh ;read high memory size + call rtc_read + mov bh,al + mov ah,cm_exl ;read low memory size + call rtc_read + mov ah,bh + pop bx +#endif + iret + ; + ; AH=C0: configuration table + ; +int15c0: mov bx,offset conf_tab ;ES:BX = @configuration table + push cs + pop es + mov ah,0 + clc + retf 2 + ; + ; AH=E8: extended memory size + ; +int15e8: mov eax,dword [cs:d_exmem] ;get extended memory size + mov ebx,eax + shr eax,10 ;convert -> 1KB blocks + cmp eax,15*1024 + jb >l1 + mov ax,15*1024 ;AX = memory 1MB to 16MB in 1KB blocks +l1: mov cx,ax ;configured = actual memory + + shr ebx,16 ;convert -> 64KB blocks + sub bx,16*16 ;subtract low 16MB + jnb >l2 ;:ok + xor bx,bx ;return 0 if less than 16MB +l2: mov dx,bx ;configured = actual memory + clc + retf 2 + ; + ; Int15 E820: get system memory map + ; +inte820: push si + cmp edx,0534d4150 ;check cookie + jnz e820_err + mov si,offset e820map ;pointer to map + inc ebx ;point to next entry + mov eax,ebx ;index -> eax +e820_1: dec eax + jz e820_2 ;:got the right si + add si,20 + cmp si,e820mape ;end of map ? + jb e820_1 +e820_err: mov ah,086 ;error code + stc + pop si + retf 2 + +e820_2: mov cx,5 ;we always copy 20 bytes + cld + push di + cs: rep movsd + pop di + mov cx,20 ;say 20 bytes + mov eax,edx ;cookie to eax + cmp si,e820mape ;end of table ? + jnz e820_3 ;:no + xor bx,bx ;clear BX = end of table +e820_3: clc + pop si + retf 2 ;ok exit + ; + ; configuration table + ; +conf_tab: dw 8 ;length + db 0fch ;model byte + db 01 ;sub model + db 00 ;BIOS level + db 70h ;cascaded interrupt 2, RTC, keyboard + ;scan hook 1A + db 0,0,0,0 ;reserved + + ; + ; INT18 entry: expansion ROM + ; +#if ! def int18 ;if not defined by user code +int18: mov si,msg_noboot ;display message "No boot device..." + call v_msg + mov ah,0 ;get a keystroke + int 16h + iret +#endif + ; + ; INT19 entry: boot operating system + ; +int19: mov byte [m_fdcnt],36 ;spin down drive A: after 2 seconds + +#if def BOOT_AC + mov dl,0 ;drive A: + mov cx,1 ;retry count + call bootdrv ;try to boot + test ah,80h ;time out ? + jnz int19_1 ;yes: probably no disk + mov dl,0 ;drive A: + mov cx,2 ;retry count + call bootdrv ;try to boot +int19_1: mov dl,80h ;drive C: + mov cx,3 ;retry count + call bootdrv ;try to boot +#else + mov dl,80h ;drive C: + mov cx,3 ;retry count + call bootdrv ;try to boot + mov dl,0 ;drive A: + mov cx,3 ;retry count + call bootdrv ;try to boot +#endif + int 18h ;display message / expansion ROM + jmp int19 + ; + ; Try to boot operating system from drive DL, CX retries + ; +bootdrv: push cx + xor ax,ax ;$0000:$7c00 = destination address + mov es,ax + mov bx,7c00h + mov ax,0201h ;read, 1 sector + mov cx,0001 ;cylinder 0, sector 1 + mov dh,0 ;head 0 + int 13h ;try to read boot sector + pop cx + jnb bootdrv2 ;:ok + push ax + mov ah,0 ;reset disk system + int 13h + pop ax ;restore status + loop bootdrv ;try 3 times +bootdrv9: ret ;return, didn't work + + ; check boot sector signature + +bootdrv2: cmp word [es:7dfeh],0aa55h + jnz bootdrv9 ;:no + jmp far 0:7c00h ;jump to boot sector + ; + ; IRQ13: coprocessor error + ; +irq13: push ax + mov al,0 ;clear error + out 0f0h,al + mov al,eoi ;end of interrupt + out pic1,al + out pic0,al + pop ax + int 2 ;NMI -> further handling + iret diff --git a/KBD.8 b/KBD.8 new file mode 100644 index 0000000..dd83ee6 --- /dev/null +++ b/KBD.8 @@ -0,0 +1,998 @@ + ; + ; Keyboard BIOS + ; + ; (C)1997-2003 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; pd 030219 add explicit wait for acknowledge, option STPC_KBD - + ; required for funky KBC in STPC Atlas and Industrial. + ; + ; Limitations: + ; + ; - Doesn't call INT15 on key wait, system request key + ; - Screen dump is called, but not implemented by BIOS. This function + ; considered risky for embedded systems, and is also highly + ; printer specific. Recommend implementation as TSR if required. + ; - We currently don't detect whether the keyboard is enhanced + ; (101 key) or not. Most keyboards are, so we set the status bit + ; kb_fkbx. + ; - XT keyboard support is not tested ! + ; + ; Note: + ; + ; - If interrupt latency is critical, recommend disabling keyboard + ; LED updates -> comment out option LED_UPDATE. + ; + + ; + ; Flag bit definitions + ; + + ; m_kbf bits + +kb_frsh equ 01h ;right shift pressed +kb_flsh equ 02h ;left shift pressed +kb_fcsh equ 04h ;control pressed +kb_fash equ 08h ;alt pressed +kb_fscrs equ 10h ;scroll lock active +kb_fnums equ 20h ;num lock active +kb_fcaps equ 40h ;caps lock active +kb_finss equ 80h ;ins active + + ; m_kbf1 bits + +kb_flct equ 01h ;left control pressed +kb_flal equ 02h ;left alt pressed +kb_fsys equ 04h ;system key pressed +kb_fhld equ 08h ;hold active +kb_fscr equ 10h ;scroll lock pressed +kb_fnum equ 20h ;num lock pressed +kb_fcap equ 40h ;caps lock pressed +kb_fins equ 80h ;ins key pressed + + ; m_kbf2 bits + +kb_fled equ 07h ;led mask +kb_fscrl equ 01h ;scroll lock led +kb_fnuml equ 02h ;num lock led +kb_fcapl equ 04h ;caps lock led +kb_fack equ 10h ;kbd ACK received +kb_fres equ 20h ;kbd RESEND received +kb_fcled equ 40h ;led update +kb_ferr equ 80h ;kbd transmit error + + ; m_kbf3 bits + +kb_fe1 equ 01h ;e1 prefix was last +kb_fe0 equ 02h ;e0 prefix was last +kb_frct equ 04h ;right control pressed +kb_fral equ 08h ;right alt pressed +kb_fkbx equ 10h ;enhanced kbd installed +kb_fnumf equ 20h ;force num lock if kbx +kb_fab equ 40h ;ab read ID was last +kb_fid equ 80h ;doing a read ID + ; + ; put keystroke in buffer + ; +putbuf: mov si,[m_kbtail] + mov di,si + inc si + inc si + cmp si,[m_kbend] ;end of buffer ? + jnz pb1 ;:no + mov si,[m_kbstart] ;:restart at beg +pb1: cmp si,[m_kbhead] ;buffer full ? + jz pbovr ;:overrun;:yes + mov [di.bofs],ax ;store keystroke + mov [m_kbtail],si + clc ;ok + ret +pbovr: stc ;overrun: return error + ret + ; + ; wait for AT kbd + ; +waitkbd: push cx + xor cx,cx ;timeout +wk1: out iowait,ax + in al,kb_stat ;read status port + test al,2 ;output buffer full ? + loopnz wk1 ;:yes + pop cx + ret + ; + ; disable AT kbd + ; +disakbd: cli + call waitkbd + mov al,0adh ;disable + out kb_stat,al + sti + ret + ; + ; send command to AT kbd + ; +kb_send: push ax ;save + push cx + mov ah,3 ;3 retries +sk1: cli + and byte [m_kbf2],kb_fled+kb_fcled+8 ;clear error bits + push ax + call waitkbd + pop ax + out kb_dat,al ;store command + sti + mov cx,2000h ;wait +sk2: test byte [m_kbf2],kb_fack+kb_fres + jnz sk4 ;:response + out iowait,ax + loop sk2 ;wait +sk3: dec ah + jnz sk1 ;:another retry + or byte [m_kbf2],kb_ferr ;set error bit + jmp short sk9 ;done + +sk4: test byte [m_kbf2],kb_fres ;resend flag ? + jnz sk3 ;:retry +sk9: cli + pop cx + pop ax + ret + ; + ; read char from kbd + ; +readchar: +#if ! def XTKBD + + call disakbd + cli + call waitkbd + in al,kb_dat ;read scan code + sti + cmp al,0feh ;resend ? + jz rch3 ;:yes + cmp al,0fah ;ack ? + jnz setled ;:no + mov al,kb_fack + jmp short rch4 + +rch3: mov al,kb_fres +rch4: cli + or byte [m_kbf2],al + pop bx + jmp done + +setled: + +#if def LED_UPDATE + cli + push dx + mov dx,pic0 + call setleds ;set mode LEDs + pop dx +#endif + sti + ret + +#else ;XT keyboard + + in al,kb_dat ;read char + xchg bx,ax + in al,port61 ;restore kbd + mov ah,al + or al,80h + out port61,al + mov al,ah + out port61,al + xchg bx,ax ;scan code -> AL + ret +#endif + +#if def LED_UPDATE + ; + ; update LEDs + ; +setleds: push ax + push cx + mov ah,[m_kbf] ;current mode flags + rol ah,4 ;-> low bits + mov al,[m_kbf2] ;current LED status + and ax,0707h ;LED bits only + cmp ah,al ;same ? + jz setled9 ;:done + test byte [m_kbf2],kb_fcled ;led update pending ? + jnz setled9 ;:yes, don't reenter + or byte [m_kbf2],kb_fcled ;set update flag + mov al,eoi ;reset interrupt controller + out dx,al ;(or iowait, depending on DX) + mov al,0edh ;set mode indicators + call kb_send ;send kbd command + +#if def STPC_KBD ;funky KBC - insist on acknowledge + + xor cx,cx +setled2: mov al,[m_kbf2] + test al,kb_ferr ;transmit error ? + jnz setled8 + test al,kb_fack ;acknowledge ? + jnz setled3 ;:yes + dec cx + jnz setled2 ;keep waiting + jmp setled8 ;bail out + +setled3: and byte [m_kbf2],!kb_fack ;clear acknowledge flag + +#else + test byte [m_kbf2],kb_ferr ;transmit error ? + jnz setled8 ;:yes +#endif + mov al,ah ;send mode + call kb_send + test byte [m_kbf2],kb_ferr ;transmit error ? + jnz setled8 ;:yes + and byte [m_kbf2],255-kb_fled ;set new state + or [m_kbf2],ah +setled8: and byte [m_kbf2],3fh ;reset update flag +setled9: pop cx + pop ax + ret +#endif + ; + ; invalid key: ignore + ; +kinval: ret + ; + ; left shift + ; +kshlt: mov al,kb_flsh +kshlt1: test byte [m_kbf3],kb_fe0 ;did we get E0 prefix ? + jnz kshlt2 ;yes: ignore (extended key) + or [m_kbf],al ;set flag + and cl,cl ;break ? + jns kshlt2 + xor [m_kbf],al ;:clear flag +kshlt2: ret + ; + ; right shift + ; +kshrt: mov al,kb_frsh + jmp kshlt1 + ; + ; left control + ; +kctlt: or byte [m_kbf1],kb_flct ;set flag + and cl,cl ;break ? + jns kctlt1 + xor byte [m_kbf1],kb_flct ;:clear flag +kctlt1: or byte [m_kbf],kb_fcsh ;set left & right flag + test byte [m_kbf1],kb_flct + jnz kctlt2 ;:ok + test byte [m_kbf3],kb_frct + jnz kctlt2 ;:ok + xor byte [m_kbf],kb_fcsh ;clear control flag + ret +kctlt2: pop ax ;don't clear hold flag + jmp i12 + ; + ; right control + ; +kctrt: test byte [m_kbf3],kb_fe0+kb_fe1 ;no E0/E1: caps lock + jz kcaps +kctrt1: or byte [m_kbf3],kb_frct ;set flag + and cl,cl ;break ? + jns kctlt1 + xor byte [m_kbf3],kb_frct ;:clear flag + jmp kctlt1 + ; + ; left alt + ; +kallt: test byte [m_kbf3],kb_fe0 ;E0: right alt + jnz kalrt + or byte [m_kbf1],kb_flal ;set flag + and cl,cl ;break ? + jns kallt1 + xor byte [m_kbf1],kb_flal ;:clear flag +kallt1: or byte [m_kbf],kb_fash ;set left & right flag + test byte [m_kbf1],kb_flal + jnz kallt2 ;:ok + test byte [m_kbf3],kb_fral + jnz kallt2 ;:ok + xor byte [m_kbf],kb_fash ;clear alt flag + xor ax,ax ;any char entered via alt ? + xchg al,[m_kbnum] + and al,al + jz kallt2 ;:no + call putbuf ;put it in buffer +kallt2: ret + ; + ; right alt + ; +kalrt: or byte [m_kbf3],kb_fral ;set flag + and cl,cl ;break ? + jns kallt1 + xor byte [m_kbf3],kb_fral ;:clear flag + jmp kallt1 + ; + ; handle toggle keys &pd fixed autorepeat 980115 + ; +kcaps: mov ch,kb_fcaps ;caps lock + jmp short ktog +kscrl: mov ch,kb_fscrs ;scroll lock + jmp short ktog +knums: mov ch,kb_fnums +ktog: and cl,cl ;break ? + jns knums2 ;:no + not ch ;clear key pressed flag + and [m_kbf1],ch +knums1: ret + +knums2: test [m_kbf1],ch ;already pressed ? + jnz knums3 ;:don't toggle again + xor [m_kbf],ch ;toggle numlock flag +knums3: or [m_kbf1],ch ;set pressed flag + ret + ; + ; pause + ; +kpaus: and cl,cl ;break ? + js knums1 ;:ignore + test byte [m_kbf1],kb_fhld ;in hold mode ? + jnz knums1 ;:yes -> ret + or byte [m_kbf1],kb_fhld ;set hold flag + mov al,eoi ;reset interrupt controller + out pic0,al + call enakbd ;enable keyboard +kpaus1: sti ;wait for next event + hlt + test byte [m_kbf1],kb_fhld ;still on ? + jnz kpaus1 ;yes: hold + pop ax ;remove return address + jmp done2 ;exit + ; + ; print screen + ; +kprts: and cl,cl + js knums1 ;:ignore break + cli + mov al,eoi ;reset interrupt controller + out pic0,al + int 5 ;do screen dump + pop ax ;remove return address + jmp done2 ;return + ; + ; reboot system + ; +kboot: mov word [m_rstflg],1234h ;set cookie + jmp far 0f000h:0fff0h ;reset jump + ; + ; system request + ; +ksysr: mov al,eoi ;reset interrupt controller + out pic0,al + mov ax,8500h + and cl,cl + jns ksysr1 ;:make + inc ax ;break code +ksysr1: int 15h ;sys req interrupt + pop ax ;remove return address + jmp done2 ;exit + ; + ; break + ; +kbrk: and cl,cl ;ignore key release + js knums1 + or byte [m_brkflg],128 ;set break flag + mov ax,[m_kbstart] ;clear kbd buffer + mov [m_kbhead],ax + mov [m_kbtail],ax + int 1bh ;break interrupt + xor ax,ax + jmp putbuf ;put break char + ; + ; alt + digit + ; +kdigtab: db 7,8,9,0,4,5,6,0,1,2,3,0 + +kdig: and cl,cl ;ignore break + js kdig1 + test byte [m_kbf3],kb_fe0 ;E0 prefix ? + jnz kdig2 ;yes: cursor keys, not Alt-number + mov al,cl + mov bx,offset kdigtab-47h + cs: xlat + mov ch,al + mov al,[m_kbnum] ;old value * 10 + mov ah,10 + mul ah + add al,ch ;add digit + mov [m_kbnum],al +kdig1: ret + +kdig2: mov ah,cl ;handle Alt-cursor keys + add ah,50h + mov al,0 + jmp putbuf + ; + ; action vector table + ; +vectab: dw kinval ;FFFF = ignore key + dw kshlt ;FFFE = left shift + dw kshrt ;FFFD = right shift + dw kctlt ;FFFC = left control + dw kctrt ;FFFB = right control + dw kallt ;FFFA = left alt + dw kalrt ;FFF9 = right alt + dw kcaps ;FFF8 = caps lock + dw knums ;FFF7 = num lock + dw kscrl ;FFF6 = scroll lock + dw kpaus ;FFF5 = pause + dw kprts ;FFF4 = print screen + dw kboot ;FFF3 = reboot system + dw ksysr ;FFF2 = system request + dw kbrk ;FFF1 = break + dw kctrt1 ;FFF0 = right control + dw kdig ;FFEF = alt + digit + ; + ; shift offset table + ; +shftab: db 1,3,3,3,5,5,5,5,7,7,7,7,9,9,9,9 + ; + ; kbd interrupt routine + ; +irq1: sti ;enable interrupt + push ax ;save registers + push bx + push cx + push dx + push si + push di + push ds + push es + cld ;forward direction + xor ax,ax ;BIOS segment + mov ds,ax + call readchar + stc ;give TSRs an opportunity to grap + mov ah,4fh ;this key: call Int15 AH=4F + int 15h + jb irq1a ;:not taken + jmp i11 ;skip this key + +irq1a: mov cl,al ;copy scan code + cmp al,0e0h ;prefix code ? + jnz i1 + or byte [m_kbf3],kb_fe0 ;set prefix flag + jmp done + +i1: cmp al,0e1h ;prefix code ? + jnz i2 + or byte [m_kbf3],kb_fe1 ;set prefix flag + jmp done + +i2: cmp al,0ffh ;overrun ? + jnz i2a ;:no + +#if def STPC_KBD + jmp done +#else + jmp overrun +#endif + +i2a: test byte [m_kbf1],kb_fhld ;hold mode ? + jz i3 ;:no + and cl,cl ;make code ? + js i3 ;no - break + xor byte [m_kbf1],kb_fhld ;clear hold mode +i3: and al,127 ;make = break + jz overrun1 ;zero: ignore + cmp al,maxscan ;too high ? + ja overrun1 ;yes: ignore char + mov ah,11 ;11 bytes per key entry + mul ah + add ax,offset kb_tab-11 ;add offset of key table + mov si,ax + mov ah,[cs:si] ;get control byte + mov al,[m_kbf] ;get shift flag + test al,kb_flsh+kb_frsh ;shift set ? + jz i4 ;:no + or al,kb_flsh+kb_frsh ;set both bits +i4: shr ah,1 ;caps lock ? + jnb i5 ;:no + test al,kb_fcaps + jnz i6 ;:set +i5: shr ah,1 ;num lock ? + jnb i7 ;:no + test byte [m_kbf3],kb_fe0 ;E0 prefix ? + jz i5a ;:no + and al,255-kb_flsh-kb_frsh ;extended key - force unshifted scan + jmp short i7 +i5a: test al,kb_fnums + jz i7 ;:not set +i6: xor al,kb_flsh+kb_frsh ;toggle shift +i7: and ax,15 + mov bx,offset shftab ;shift state + cs: xlat + xchg bx,ax ;-> entry offset + mov ax,[cs:bx+si] ;get scan/action code + cmp ax,vecmin + jb ikey ;:scan code + not ax ;action key: convert to jump vector + shl ax,1 + xchg bx,ax + + ; Dispatch special keys. + + mov bx,[cs:bx+vectab] ;get vector of special key handler + call bx ;call special key handler +iact2: jmp short i11 ;done + +overrun1: jmp short overrun + +ikey: and cl,cl ;is it break ? + js i11 ;yes: ignore + + test byte [m_kbf3],kb_fe0 ;E0 prefix ? + jz ikey9 ;:no + test al,al + jnz ikey4 + + cmp ah,96h ;Ctrl * -> Ctrl PrtSc + jnz ikey1 + mov ah,72h + jmp short ikey9 + +ikey1: cmp ah,1ch ;Alt keypad enter -> A600 + jnz ikey2 + mov ah,0a6h + +ikey2: cmp ah,35h ;Alt keypad -> A400 + jnz ikey3 + mov ah,0a4h + +ikey3: cmp ah,84h ;high extended keys -> no change + jae ikey9 + mov al,0e0h ;remember this was a extended key + jmp short ikey9 + +ikey4: cmp ah,1ch ;keypad enter ? + jz ikey8 + cmp ah,35h ;keypad / ? + jnz ikey9 +ikey8: mov ah,0e0h ;extended key, translated back by + ;kb_xlat +ikey9: call putbuf ;put scan code in buffer + jnb i11 ;:ok +overrun: cli + mov al,eoi ;reset interrupt controller + out pic0,al + call beep + jmp short done2 +i11: +i12: and byte [m_kbf3],255-kb_fe0-kb_fe1 ;reset prefix flag + +#if ! def XTKBD ;if AT + + cli + mov al,eoi ;reset interrupt controller + out pic0,al + call enakbd + jmp short done3 +#endif + +done: cli + mov al,eoi ;reset interrupt controller + out pic0,al +done2 : + +#if ! def XTKBD + call enakbd +#endif + +done3: pop es + pop ds + pop di + pop si + pop dx + pop cx + pop bx + pop ax + iret + ; + ; INT 16 entry + ; +int16: sti ;enable interrupts + push bx + push cx + push dx + push ds + + xor dx,dx ;access BIOS segment + mov ds,dx + add dl,ah ;command code + jz kb_get ;AH=00: get key + dec dx + jz kb_check ;AH=01: check if key available + dec dx + jz kb_shift ;AH=02: return shift status + dec dx + jz kb_rate ;AH=03: set repetition rate + dec dx + dec dx +#if def STPC_KBD + jnz int16b + jmp kb_write ;AH=05: place scan code in buffer +int16b: +#else + jz kb_write ;AH=05: place scan code in buffer +#endif + sub dl,11 + jz kb_extrd ;AH=10: extended read + dec dx + jz kb_extst ;AH=11: extended status + dec dx + jz kb_extsh ;AH=12: extended shift status +kb_exit: pop ds + pop dx + pop cx + pop bx + iret + ; + ; AH=00: get key from buffer + ; +kb_get: call kb_getch ;get character from buffer + call kb_xlat ;translate extended characters + jb kb_get ;:extended character, try again + jmp kb_exit + ; + ; AH=01: check if key available + ; +kb_check0: call kb_getch ;skip extended character +kb_check: call kb_chk ;check for character + jz kb_exitst ;:nothing available + call kb_xlat ;check if extended character + jb kb_check0 ;:extended character, skip +kb_extst2: inc dx ;clear Z flag +kb_exitst: pop ds ;exit, flags modified + pop dx + pop cx + pop bx + retf 2 + ; + ; AH=10: extended read + ; +kb_extrd: call kb_getch ;get character + call kb_exlat ;convert extended codes + jmp kb_exit + ; + ; AH=11: extended status + ; +kb_extst: call kb_chk ;check status + jz kb_exitst ;:nothing, return Z flag + call kb_exlat + jmp kb_extst2 ;return result + ; + ; AH=12: extended shift status + ; +kb_extsh: xor ah,ah + mov al,[m_kbf1] ;system request shift + test al,kb_fsys ;system request ? + jz kb_extsh2 + mov ah,80h ;yes: set bit 7 +kb_extsh2: and al,01110011xb + or ah,al + mov al,[m_kbf3] ;right control and alt keys + and al,00001100xb + or ah,al + ; + ; AH=02: return current shift status + ; +kb_shift: mov al,[m_kbf] ;get shift status + jmp kb_exit + ; + ; AH=03: set key repetition rate + ; +kb_rate: cmp al,5 ;correct command ? + jnz kb_exit + cmp bl,31 ;test rate + ja kb_exit + cmp bh,3 ;test delay + ja kb_exit + push ax ;save AX + shl bh,5 + mov al,0f3h ;set repeat rate / delay command + call kb_send ;send to keyboard + +#if def STPC_KBD ;funky KBC - insist on acknowledge + + xor cx,cx +kbrate2: mov al,[m_kbf2] + test al,kb_ferr ;transmit error ? + jnz kbrate8 + test al,kb_fack ;acknowledge ? + jnz kbrate3 ;:yes + dec cx + jnz kbrate2 ;keep waiting + jmp kbrate8 ;bail out + +kbrate3: and byte [m_kbf2],!kb_fack ;clear acknowledge flag + +#endif + + mov al,bl ;combine delay, rate + add al,bh + call kb_send +kbrate8: pop ax ;restore AX + jmp kb_exit + ; + ; AH=05: place scan code in buffer + ; +kb_write: mov al,1 ;error status + cli ;prevent conflict + mov bx,[m_kbtail] ;^kb buffer + inc bx ;increment + inc bx + cmp bx,[m_kbend] ;at end ? + jnz kb_write2 + mov bx,[m_kbstart] ;yes: go to start +kb_write2: cmp bx,[m_kbhead] ;buffer full ? + jz kb_write3 ;:yes + xchg bx,[m_kbtail] ;update tail, get old value + dec ax ;clear AL + mov [bx+bofs],cx ;store scan code +kb_write3: sti ;end of critical section + jmp kb_exit + ; + ; get scan code from buffer + ; +kb_getch0: sti ;reenable interrupts + hlt ;wait for next event +kb_getch: cli ;critical section + mov bx,[m_kbhead] ;^head of buffer + cmp bx,[m_kbtail] ;= tail of buffer ? + jz kb_getch0 ;yes: wait + mov ax,[bx+bofs] ;get scan code + inc bx ;increment pointer + inc bx + cmp bx,[m_kbend] ;at end ? + jnz kb_getch2 + mov bx,[m_kbstart] ;yes: go to start +kb_getch2: mov [m_kbhead],bx ;update pointer + sti ;end of critical section + ret + ; + ; check if there is anything in buffer (Z set if not) + ; +kb_chk: cli ;critical section + mov bx,[m_kbhead] ;^head of buffer + cmp bx,[m_kbtail] ;= tail of buffer ? + sti ;end of critical section + mov ax,[bx+bofs] ;get scan code + ret + ; + ; check if extended character, set C if yes + ; +kb_xlat: cmp ah,84h ;extended ? + jbe kb_xlat83 + cmp ah,0e0h + jnz kb_stc1 ;:bad + mov ah,1ch ;keypad Enter fixed code + cmp al,13 ;keypad Enter ? + jz kb_xlatok ;:yes + cmp al,10 ;keypad ^Enter + jz kb_xlatok ;:yes + mov ah,35h ;keypad / + jmp short kb_xlatok + +kb_xlat83: cmp ax,00e0h ;extension ? + jz kb_xlatok + cmp ax,00f0h + jz kb_xlatok + cmp al,0f0h ;fill-in ? + jz kb_stc1 + cmp al,0e0h + jnz kb_xlatok + mov al,0 +kb_xlatok: clc ;ok to use + ret + +kb_stc1: stc ;extended code - bad + ret + ; + ; translate extended characters + ; +kb_exlat: cmp al,0f0h ;special ? + jnz kb_exlat2 + or ah,ah ;0: more special + jz kb_exlat2 + mov al,0 +kb_exlat2: ret + ; + ; initialize keyboard controller + ; +kb_ini: + +#if ! def XTKBD + +#if def NO_KBC ;bail quickly if no KBC present + in al,kb_stat ;check status + cmp al,0ffh ;nothing ? + jnz kb_ini0 + mov byte [tmp_kbc],0ffh ;set flag - KBC not present + stc + ret +kb_ini0: +#endif + xor cx,cx +kb_ini1: in al,kb_stat ;check status + mov bl,al + and al,1 ;buffer full ? + jz kb_ini2 + in al,kb_dat ;flush data +kb_ini2: and bl,2 + jz kb_ini3 ;:empty + loop kb_ini1 +kb_ini9: stc ;error + ret + +kb_ini3: mov al,0aah ;self test command + call kb_cmd + jb kb_ini9 ;:timeout + call kb_read ;wait for data + jb kb_ini9 + cmp al,55h ;expect $55 response + jnz kb_ini9 + + mov al,0abh ;test interface + call kb_cmd + jb kb_ini9 ;:timeout + call kb_read ;wait for data + jb kb_ini9 + cmp al,0 ;expect 0 response + jnz kb_ini9 + + mov al,60h ;write mode register + call kb_cmd + jb kb_ini9 + mov al,6dh ;initial mode (keep system flag off, + ;disable mouse interface) + call kb_writ + jb kb_ini9 + +#else ;XT keyboard + + in al,port61 + out iowait,ax + or al,0c0h ;set reset bit + out port61,al + out iowait,ax + and al,7fh ;clear reset bit + out port61,al +#endif +kb_clc1: clc ;ok + ret + ; + ; send keyboard command AL + ; +kb_cmd: out kb_stat,al ;send command +kb_cmd1: out iowait,ax + ; + ; wait until input (to 8042) buffer empty, C if timeout + ; +kb_ibf: xor cx,cx +kb_ibf2: out iowait,ax + in al,kb_stat + and al,2 ;input buffer full ? + jz kb_clc1 ;:ok return + loop kb_ibf2 + stc + ret + ; + ; send keyboard data AL + ; +kb_writ: out kb_dat,al + jmp kb_cmd1 + ; + ; wait until output (from 8042) buffer full, read data -> AL + ; +kb_read: xor cx,cx +kb_obf2: out iowait,ax + in al,kb_stat + and al,1 ;output buffer full ? + jnz kb_obf3 + loop kb_obf2 + stc + ret + +kb_obf3: in al,kb_dat + clc + ret + ; + ; second keyboard initialization (after base memory test) + ; +kb_inb: + +#if def NO_KBC ;skip if no KBC present + ror byte [tmp_kbc],1 + jb kb_inb9 +#endif + + call kb_ibf ;wait for 8042 ready + + in al,kb_stat ;anything in 8042 output buffer ? + and al,1 + jz kb_inb2 ;:no + in al,kb_dat + mov [tmp_kbd],al ;save keyboard response + cmp al,0aah ;keyboard test ok ? + jz kb_inb9 ;:yes, don't reset again + +kb_inb2: mov al,0ffh ;reset keyboard + out kb_dat,al + call kb_read ;get acknowledge + +kb_inb9: mov byte [m_kbf],kb_fnums ;set Numlock + mov byte [m_kbf3],kb_fkbx ;assume enhanced keyboard + mov ax,m_kbbuf-bofs ;initialize keyboard buffer pointers + mov [m_kbstart],ax + mov [m_kbhead],ax + mov [m_kbtail],ax + mov word [m_kbend],m_kbbuf9-bofs + ret + ; + ; third keyboard initialization (after extended memory test) + ; +kb_inc: +#if def NO_KBC ;skip if no KBC present + ror byte [tmp_kbc],1 + jnb kb_inc1 + ret +kb_inc1: +#endif + cmp byte [tmp_kbd],0aah ;keyboard reset ok ? + jz kb_inc2 ;:yes + call kb_read ;wait until data in buffer + jb kb_err + cmp al,0aah ;AA = keyboard response + jz kb_inc2 ;:ok +kb_err: inc byte [tmp_kbfail] +#if ! def NO_KBC + mov si,offset msg_kbd ;"Keyboard failure" + call v_msg +#endif +kb_inc2: call kb_ibf ;wait for 8042 ready + mov al,0f4h ;enable keyboard + out kb_dat,al + jmp kb_read ;get acknowledge + ; + ; set keyboard LEDs, enable keyboard + ; +kb_ind: +#if def NO_KBC ;skip if no KBC present + ror byte [tmp_kbc],1 + jnb kb_ind1 + ret +kb_ind1: +#endif + +#if def KEY_RATE + mov ax,0305h ;set keyboard repeat rate + mov bx,KEY_RATE + int 16h +#endif + + call disakbd ;disable keyboard interface + +#if def LED_UPDATE + mov dx,iowait + call setleds ;set keyboard LEDs +#endif + ;-> fall through + ; + ; enable AT kbd + ; +enakbd: cli + call waitkbd + mov al,0aeh + out kb_stat,al + sti + ret diff --git a/KBTAB.8 b/KBTAB.8 new file mode 100644 index 0000000..c593145 --- /dev/null +++ b/KBTAB.8 @@ -0,0 +1,231 @@ + ; + ; US ASCII keyboard layout table + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + + ; + ; key action codes (don't change !!!) + ; +k_nil equ 0ffffh ;ignore key +k_lsh equ 0fffeh ;left shift +k_rsh equ 0fffdh ;right shift +k_lct equ 0fffch ;left control +k_rct equ 0fffbh ;right control +k_alt equ 0fffah ;left alt +k_ral equ 0fff9h ;right alt +k_cap equ 0fff8h ;caps lock +k_num equ 0fff7h ;num lock +k_scr equ 0fff6h ;scroll lock +k_pau equ 0fff5h ;pause +k_prt equ 0fff4h ;print screen +k_boo equ 0fff3h ;reboot system +k_sys equ 0fff2h ;system request +k_brk equ 0fff1h ;break +k_rct1 equ 0fff0h ;right control +k_dig equ 0ffefh ;alt + digit +vecmin equ 0ffefh ;minimal action code + ; + ; US ASCII keyboard layout + ; + ; key entry structure: + ; + ; 0: control byte 0 = nothing special + ; 1 = check caps lock + ; 2 = check num lock if not E0 prefix + ; 1: normal scan code + ; 3: shift scan code + ; 5: control scan code + ; 7: alt scan code + ; 9: ctrl-alt scan code + ; +ct equ -0040h ;offset for control characters +kb_tab db 0 ;01 esc + dw 011bh,011bh,011bh,0100h,k_nil + db 0 ;02 1 + dw 0200h+"1",0200h+"!",k_nil,7800h,k_nil + db 0 ;03 2 + dw 0300h+"2",0300h+"@",0300h,7900h,k_nil + db 0 ;04 3 + dw 0400h+"3",0400h+"#",k_nil,7a00h,k_nil + db 0 ;05 4 + dw 0500h+"4",0500h+"$",k_nil,7b00h,k_nil + db 0 ;06 5 + dw 0600h+"5",0600h+"%",k_nil,7c00h,k_nil + db 0 ;07 6 + dw 0700h+"6",0700h+"^",071eh,7d00h,k_nil + db 0 ;08 7 + dw 0800h+"7",0800h+"&",k_nil,7e00h,k_nil + db 0 ;09 8 + dw 0900h+"8",0900h+"*",k_nil,7f00h,k_nil + db 0 ;0a 9 + dw 0a00h+"9",0a00h+"(",k_nil,8000h,k_nil + db 0 ;0b 0 + dw 0b00h+"0",0b00h+")",k_nil,8100h,k_nil + db 0 ;0c - + dw 0c00h+"-",0c00h+"_",0c1fh,8200h,k_nil + db 0 ;0d = + dw 0d00h+"=",0d00h+"+",k_nil,8300h,k_nil + db 0 ;0e bs + dw 0e08h,0e08h,0e7fh,0e00h,k_nil + db 0 ;0f tab + dw 0f09h,0f00h,9400h,0a500h,k_nil + db 1 ;10 q + dw 1000h+"q",1000h+"Q",1000h+ct+"Q",1000h,k_nil + db 1 ;11 w + dw 1100h+"w",1100h+"W",1100h+ct+"W",1100h,k_nil + db 1 ;12 e + dw 1200h+"e",1200h+"E",1200h+ct+"E",1200h,k_nil + db 1 ;13 r + dw 1300h+"r",1300h+"R",1300h+ct+"R",1300h,k_nil + db 1 ;14 t + dw 1400h+"t",1400h+"T",1400h+ct+"T",1400h,k_nil + db 1 ;15 y + dw 1500h+"y",1500h+"Y",1500h+ct+"Y",1500h,k_nil + db 1 ;16 u + dw 1600h+"u",1600h+"U",1600h+ct+"U",1600h,k_nil + db 1 ;17 i + dw 1700h+"i",1700h+"I",1700h+ct+"I",1700h,k_nil + db 1 ;18 o + dw 1800h+"o",1800h+"O",1800h+ct+"O",1800h,k_nil + db 1 ;19 p + dw 1900h+"p",1900h+"P",1900h+ct+"P",1900h,k_nil + db 0 ;1a [ + dw 1a00h+"[",1a00h+"{",1a00h+ct+"[",1a00h,k_nil + db 0 ;1b ] + dw 1b00h+"]",1b00h+"}",1b00h+ct+"]",1b00h,k_nil + db 0 ;1c cr / e0 keypad enter + dw 1c0dh,1c0dh,1c0ah,1c00h,k_nil + db 0 ;1d left control, e0 right ctrl + dw k_lct,k_lct,k_lct,k_lct,k_lct + db 1 ;1e a + dw 1e00h+"a",1e00h+"A",1e00h+ct+"A",1e00h,k_nil + db 1 ;1f s + dw 1f00h+"s",1f00h+"S",1f00h+ct+"S",1f00h,k_nil + db 1 ;20 d + dw 2000h+"d",2000h+"D",2000h+ct+"D",2000h,k_nil + db 1 ;21 f + dw 2100h+"f",2100h+"F",2100h+ct+"F",2100h,k_nil + db 1 ;22 g + dw 2200h+"g",2200h+"G",2200h+ct+"G",2200h,k_nil + db 1 ;23 h + dw 2300h+"h",2300h+"H",2300h+ct+"H",2300h,k_nil + db 1 ;24 j + dw 2400h+"j",2400h+"J",2400h+ct+"J",2400h,k_nil + db 1 ;25 k + dw 2500h+"k",2500h+"K",2500h+ct+"K",2500h,k_nil + db 1 ;26 l + dw 2600h+"l",2600h+"L",2600h+ct+"L",2600h,k_nil + db 0 ;27 ; + dw 2700h+";",2700h+":",k_nil,2700h,k_nil + db 0 ;28 ' + dw 2800h+"'",2822h,k_nil,2800h,k_nil + db 0 ;29 tilde + dw 2900h+"`",2900h+"~",k_nil,k_nil,k_nil + db 0 ;2a left shift + dw k_lsh,k_lsh,k_lsh,k_lsh,k_lsh + db 0 ;2b \ + dw 2b00h+"\",2b00h+"|",2b00h+ct+"\",2b00h,k_nil + db 1 ;2c z + dw 2c00h+"z",2c00h+"Z",2c00h+ct+"Z",2c00h,k_nil + db 1 ;2d x + dw 2d00h+"x",2d00h+"X",2d00h+ct+"X",2d00h,k_nil + db 1 ;2e c + dw 2e00h+"c",2e00h+"C",2e00h+ct+"C",2e00h,k_nil + db 1 ;2f v + dw 2f00h+"v",2f00h+"V",2f00h+ct+"V",2f00h,k_nil + db 1 ;30 b + dw 3000h+"b",3000h+"B",3000h+ct+"B",3000h,k_nil + db 1 ;31 n + dw 3100h+"n",3100h+"N",3100h+ct+"N",3100h,k_nil + db 1 ;32 m + dw 3200h+"m",3200h+"M",3200h+ct+"M",3200h,k_nil + db 0 ;33 , + dw 3300h+",",3300h+"<",k_nil,3300h,k_nil + db 0 ;34 . + dw 3400h+".",3400h+">",k_nil,3400h,k_nil + db 0 ;35 / e0 keypad / 002f 002f 9500 a400 ffff & + dw 3500h+"/",3500h+"?",9500h,3500h,k_nil + db 0 ;36 right shift + dw k_rsh,k_rsh,k_rsh,k_rsh,k_rsh + db 0 ;37 keypad * + dw 3700h+"*",k_prt,9600h,3700h,k_nil + db 0 ;38 left alt e0 right alt + dw k_alt,k_alt,k_alt,k_alt,k_alt + db 0 ;39 space + dw 3900h+" ",3900h+" ",3900h+" ",3900h+" ",k_nil + db 0 ;3a caps lock + dw k_cap,k_cap,k_cap,k_cap,k_cap + db 0 ;3b F1 + dw 3b00h,5400h,5e00h,6800h,k_nil + db 0 ;3c F2 + dw 3c00h,5500h,5f00h,6900h,k_nil + db 0 ;3d F3 + dw 3d00h,5600h,6000h,6a00h,k_nil + db 0 ;3e F4 + dw 3e00h,5700h,6100h,6b00h,k_nil + db 0 ;3f F5 + dw 3f00h,5800h,6200h,6c00h,k_nil + db 0 ;40 F6 + dw 4000h,5900h,6300h,6d00h,k_nil + db 0 ;41 F7 + dw 4100h,5a00h,6400h,6e00h,k_nil + db 0 ;42 F8 + dw 4200h,5b00h,6500h,6f00h,k_nil + db 0 ;43 F9 + dw 4300h,5c00h,6600h,7000h,k_nil + db 0 ;44 F10 + dw 4400h,5d00h,6700h,7100h,k_nil + db 0 ;45 num lock + dw k_num,k_num,k_pau,k_num,k_num + db 0 ;46 scroll lock + dw k_scr,k_scr,k_brk,k_scr,k_scr + db 2 ;47 home + dw 4700h,4700h+"7",7700h,k_dig,k_nil + db 2 ;48 up + dw 4800h,4800h+"8",8d00h,k_dig,k_nil + db 2 ;49 page up + dw 4900h,4900h+"9",8400h,k_dig,k_nil + db 0 ;4a keypad - + dw 4a00h+"-",4a00h+"-",8e00h,4a00h,k_nil + db 2 ;4b left + dw 4b00h,4b00h+"4",7300h,k_dig,k_nil + db 2 ;4c center + dw 4c00h,4c00h+"5",8f00h,k_dig,k_nil + db 2 ;4d right + dw 4d00h,4d00h+"6",7400h,k_dig,k_nil + db 0 ;4e keypad + + dw 4e00h+"+",4e00h+"+",9000h,4e00h,k_nil + db 2 ;4f end + dw 4f00h,4f00h+"1",7500h,k_dig,k_nil + db 2 ;50 down + dw 5000h,5000h+"2",9100h,k_dig,k_nil + db 2 ;51 page down + dw 5100h,5100h+"3",7600h,k_dig,k_nil + db 2 ;52 ins + dw 5200h,5200h+"0",9200h,k_dig,k_nil + db 2 ;53 delete + dw 5300h,5300h+".",9300h,0a300h,k_boo + db 0 ;54 print screen / sys req + dw k_prt,k_nil,7200h,k_sys,k_nil + db 0 ;55 no key + dw k_nil,k_nil,k_nil,k_nil,k_nil + db 0 ;56 left shift + dw k_lsh,k_lsh,k_lsh,k_lsh,k_lsh + db 0 ;57 F11 + dw 8500h,8700h,8900h,8b00h,k_nil + db 0 ;58 F12 + dw 8600h,8800h,8a00h,8c00h,k_nil + db 0 ;59 no key + dw k_nil,k_nil,k_nil,k_nil,k_nil + db 0 ;5A no key + dw k_nil,k_nil,k_nil,k_nil,k_nil + db 0 ;5B windows key left (104 key kbd only) + dw k_nil,k_nil,k_nil,k_nil,k_nil + db 0 ;5C windows key right (104 key kbd) + dw k_nil,k_nil,k_nil,k_nil,k_nil + db 0 ;5D menu key (104 key kbd) + dw k_nil,k_nil,k_nil,k_nil,k_nil + +maxscan equ 5Dh diff --git a/LPT.8 b/LPT.8 new file mode 100644 index 0000000..a861a50 --- /dev/null +++ b/LPT.8 @@ -0,0 +1,127 @@ + ; + ; Printer BIOS + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; Limitations: + ; + ; - Doesn't call INT15 function 90FE on busy wait. + ; + ; pd 991003 fixed lp_test loop + ; + ; INT 17 entry + ; +int17: sti ;reenable interrupts + push ds ;save registers + push si + push bx + push cx + push dx + mov bx,ax ;save AX + xor ax,ax ;access BIOS segment + mov ds,ax + cmp dx,3 ;max port ? + jae lp_exit ;:return + mov si,dx ;-> table index + mov cl,[si+m_lptime] ;get time-out value + shl si,1 + mov dx,[si+m_lpio] ;get I/O port base + and dx,dx ;0 -> not present + jz lp_exit + mov al,bh ;get command code + and al,al + jz lp_out ;AH=0 -> output character + dec ax + jz lp_init ;AH=1 -> initialize + dec ax + jz lp_stat ;AH=2 -> get status +lp_exit: mov al,bl ;restore AL + pop dx ;restore registers + pop cx + pop bx + pop si + pop ds + iret + ; + ; AH=00: output character + ; +lp_out: mov al,bl ;output character + out dx,al ;[DX+0] + inc dx + xor si,si +lp_wait: in al,dx ;[DX+1] get status + mov ah,al + and al,al ;busy ? + js lp_ok ;:no + dec si + jnz lp_wait + dec cl + jnz lp_wait + or ah,1 ;time-out + and ah,0f9h + jmp short lp_out2 ;flip bits, exit + +lp_ok: inc dx + mov al,0dh ;activate strobe + out dx,al ;[DX+2] + out iowait,ax +lp_in2: mov al,0ch ;deactivate strobe + out dx,al ;[DX+2] + dec dx + dec dx + out iowait,ax + ; + ; get printer status + ; +lp_stat: inc dx ;get status + in al,dx ;[DX+1] + and al,0f8h + mov ah,al +lp_out2: mov al,bl ;restore AL + xor ah,048h + jmp lp_exit + ; + ; initialize printer + ; +lp_init: mov al,8 ;reset printer + inc dx + inc dx + out dx,al ;[DX+2] + mov cx,5000 ;wait a bit +lp_in1: loop lp_in1 + jmp lp_in2 + ; + ; test printer ports + ; +lp_test: mov ax,1414h ;init printer time-out + mov [m_lptime],ax + mov [m_lptime+2],ax + mov si,offset lp_ports + mov di,offset m_lpio ;destination for I/O port +lp_test0: cs: lodsw ;get I/O port to test + and ax,ax + jz lp_test0a ;:end of table + xchg dx,ax ;AX -> DX port address + call lp_test2 ;test the port + jmp lp_test0 ;try next + +lp_test0a: mov dx,2 +lp_test1: mov ah,1 ;init port + int 17h + dec dx + jns lp_test1 + ret + +lp_test2: mov ax,0aa55h ;write test pattern + out dx,al + out iowait,ax ;invert bus + out iowait,ax + in al,dx ;read back test pattern + cmp al,55h ;correct ? + jnz lp_test9 ;:no printer + mov [di],dx ;store port address + inc di + inc di + add byte [m_devflg+1],40h ;count printer ports in equipment flag +lp_test9: ret diff --git a/MAKE.BAT b/MAKE.BAT new file mode 100644 index 0000000..93455b7 --- /dev/null +++ b/MAKE.BAT @@ -0,0 +1,7 @@ +@rem This is for fill patterns only - main BIOS files are part of chipset +@rem directories. +@rem +a386 fill16.8 fill16.bin +a386 fill32.8 fill32.bin +a386 fill48.8 fill48.bin +del fill*.sym diff --git a/MESSAGE.8 b/MESSAGE.8 new file mode 100644 index 0000000..9136753 --- /dev/null +++ b/MESSAGE.8 @@ -0,0 +1,25 @@ + ; + ; BIOS messages + ; + ; (C)1997-2003 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; DO NOT DELETE the following string ! (does not need to be displayed) + db "tinyBIOS V1.4a (C)1997-2005 PC Engines",13,10,10,0 + ; + ; Error messages + ; +msg_parit: db "Parity Error !",13,10,0 +msg_iochk: db "I/O channel NMI !",13,10,0 +msg_halt: db "System halted.",0 +msg_noboot: db "No boot device available, press Enter to continue.",13,10,0 +msg_base: db " KB Base Memory",13,10,0 +msg_ext: db " KB Extended Memory",13,10,0 +msg_crlf: db 13,10,0 +msg_kbd: db "Keyboard failure.",13,10,0 +msg_bs: db 8,8,8,8,8,0 + +#if def HD_WAIT +msg_wait: db "Waiting for HDD " +msg_dot: db ".",0 +#endif diff --git a/PCI.8 b/PCI.8 new file mode 100644 index 0000000..1d59b53 --- /dev/null +++ b/PCI.8 @@ -0,0 +1,532 @@ + ; + ; PCI BIOS + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; pd 021021 fix 32 bit call 03 -> pci3class instead of pci3find + ; (fix courtesy Mark Dieck) + ; pd 990525 rewrite 32 bit BIOS to avoid any data accesses + ; (Linux hang) + ; + ; Limitations: + ; + ; - Interrupt probe / reassign functions not implemented. + ; + ; I/O ports + ; +pci_ver equ 0210h ;PCI version reported +bios3seg equ 000fh ;$f000 / 4096 + ; + ; 32 bit pusha stack frame + ; +_eax equ 1ch +_ebx equ 10h +_ecx equ 18h +_edx equ 14h +_ebp equ 08h +_esi equ 04h +_edi equ 00h +_efl equ 24h ;flags + ; + ; PCI INT1A functions + ; +pci_i1a: pushad ;build stack frame + mov bp,sp + mov ah,0 ;command code -> vector + cmp al,0fh ;max command + ja pci_badc0 ;:bad + add ax,ax + mov si,ax + call [cs:si+pci_vec] ;dispatch command +pci_i1a2: jb pci_i1a4 ;:error + mov al,0 ;ok status + and byte [bp._efl],0feh ;clear carry flag +pci_i1a3: mov byte [bp._eax+1],al ;return status code -> AH + popad + iret + ; + ; bad command + ; +pci_badc: pop ax ;pop return address +pci_badc0: mov al,81h ;bad command + ; + ; return error + ; +pci_i1a4: or byte [bp._efl],1 ;set carry flag + jmp pci_i1a3 + ; + ; dispatch table + ; + even +pci_vec: dw pci_badc ;00: bad command + dw pci_pres ;01: PCI BIOS present + dw pci_find ;02: find PCI device + dw pci_class ;03: find PCI class code + dw pci_badc ;04: invalid + dw pci_badc ;05: invalid + dw pci_spec ;06: generate special cycle + dw pci_badc ;07: invalid + dw pci_readb ;08: read config byte + dw pci_readw ;09: read config word + dw pci_readd ;0A: read config dword + dw pci_writb ;0B: write config byte + dw pci_writw ;0C: write config word + dw pci_writd ;0D: write config dword + dw pci_badc ;0E: get IRQ routing options + ;not implemented + dw pci_badc ;0F: set PCI IRQ + ;not implemented + ; + ; 01: PCI BIOS present + ; +pci_pres: mov word [bp._ebx],pci_ver ;PCI 2.1 -> BX + mov dword [bp._edx],20494350h ;"PCI " -> DX + mov al,byte [cs:d_lastbus] ;number of last PCI bus -> CL + mov byte [bp._ecx],al + mov byte [bp._eax],11h ;hardware mechanism 1 -> AL + clc + ret + ; + ; 08: read config byte + ; +pci_readb: mov ax,0ff00h ;address mask (forbidden bits) + call pci_idx ;set index + in al,dx ;read data + mov [bp._ecx],al ;return in CL + ret + ; + ; 09: read config word + ; +pci_readw: mov ax,0ff01h ;address mask + call pci_idx ;set index + in ax,dx ;read data + mov [bp._ecx],ax ;return in CX + ret + ; + ; 0A: read config dword + ; +pci_readd: mov ax,0ff03h ;address mask + call pci_idx ;set index + in eax,dx ;read data + mov [bp._ecx],eax ;return in ECX + ret + ; + ; 0B: write config byte + ; +pci_writb: mov ax,0ff00h ;address mask + call pci_idx ;set index + out dx,al ;write data + ret + ; + ; 0C: write config word + ; +pci_writw: mov ax,0ff01h ;address mask + call pci_idx ;set index + out dx,ax ;write data + ret + ; + ; 06: generate special cycle + ; +pci_spec: cmp bh,byte [cs:d_lastbus] ;bus number ok ? + ja pci_badc ;:bad + mov bl,0ffh ;device number = FF + mov ecx,edx ;special cycle data -> ECX + xor di,di ;register number 0 + ;fall through + ; + ; 0D: write config dword + ; +pci_writd: mov ax,0ff03h ;address mask + call pci_idx ;set index + out dx,eax ;write data + ret + ; + ; set PCI configuration index + ; +pci_idx: test di,ax ;any "forbidden" bits set ? + jnz pci_idx1 + mov dx,pci_ad + mov ah,80h ;configuration enable + mov al,bh ;bus number + shl eax,16 ;-> high word + mov ax,di ;register number + and al,0fch ;clear lower bits + mov ah,bl ;device number / function number + out dx,eax ;set index + mov dl,low(pci_dat) + and di,3 ;low bits of register + add dx,di ;update register pointer (implicit clc) + mov eax,ecx ;write data -> EAX + ret + +pci_idx1: pop ax ;return address + mov al,87h ;bad register number + stc + ret ;return to dispatcher + ; + ; 02: find PCI device + ; +pci_find: inc dx ;vendor ID FFFF ? + jnz pci_find2 ;:ok + mov al,83h ;bad vendor ID + stc + ret + +pci_find2: dec dx ;restore vendor ID + mov ebx,80000000h ;bus address + shl ecx,16 ;device ID -> bits 31..16 + mov cx,dx ;vendor ID -> bits 15..00 + mov esi,0ffffffffh ;mask + jmp short pci_find3 + ; + ; 03: find PCI class code + ; +pci_class: shl ecx,8 ;class code is bits 31..08 + mov esi,0ffffff00h ;mask + mov ebx,80000000h+p_class ;bus address + +pci_find3: mov di,[bp._esi] ;restore device index + ; + ; search all buses / devices + ; +pci_find4: mov dx,pci_ad + mov eax,ebx ;device address + mov al,p_id ;vendor / device ID + out dx,eax + mov dl,low(pci_dat) + in eax,dx ;read device / vendor ID + cmp ax,0ffffh ;not present ? + jz pci_find7 ;:skip entire device + cmp bl,0 ;looking for class code ? + jz pci_find5 ;:no + mov dl,low(pci_ad) + mov eax,ebx ;restore register offset + out dx,eax + mov dl,low(pci_dat) + in eax,dx ;read class code +pci_find5: and eax,esi ;mask relevant bits + cmp eax,ecx ;same ? + jnz pci_find6 ;:no + dec di ;device count + js pci_found ;:this is the one + + ; try next function + +pci_find6: test bh,7 ;function 0 ? + jnz pci_find8 ;:no + mov dl,low(pci_ad) + mov eax,ebx ;index + mov al,p_hedt and 0fch ;header type + out dx,eax + mov dl,low(pci_dat) + 2 + in al,dx ;read header type + test al,80h ;multifunction device ? + jnz pci_find8 ;:yes +pci_find7: or bh,7 ;skip the rest of this device + + ; try next device / function + +pci_find8: inc bh ;next device / function + jnz pci_find4 ;:ok + + ; try next bus + + ror ebx,16 + inc bx ;next bus + cmp bl,byte [cs:d_lastbus] + ja pci_find9 ;:not found + ror ebx,16 ;restore + jmp pci_find4 + + ; didn't find it + +pci_find9: mov al,86h ;device not found + stc + ret + + ; found device + +pci_found: shr ebx,8 ;return bus, device number in BX + mov [bp._ebx],bx + clc ;ok return + ret + +#if ! def NO_PCI32 + ; + ; 32 bit PCI BIOS entry point + ; + use32 + +pci_32: pushad ;build stack frame + mov ebp,esp + + cmp al,0ah ;0A: read config dword + jnz pci_3a + call pci3readd + jmp short pci_3z + +pci_3a: cmp al,09 ;09: read config word + jnz pci_3b + call pci3readw + jmp short pci_3z + +pci_3b: cmp al,08 ;08: read config byte + jnz pci_3c + call pci3readb + jmp short pci_3z + +pci_3c: cmp al,0dh ;0D: write config dword + jnz pci_3d + call pci3writd + jmp short pci_3z + +pci_3d: cmp al,0ch ;0C: write config word + jnz pci_3e + call pci3writw + jmp short pci_3z + +pci_3e: cmp al,0bh ;0B: write config byte + jnz pci_3f + call pci3writb + jmp short pci_3z + +pci_3f: cmp al,01 ;01: PCI BIOS present + jnz pci_3g + call pci3pres + jmp short pci_3z + +pci_3g: cmp al,02 ;02: find PCI device + jnz pci_3h + call pci3find + jmp short pci_3z + +pci_3h: cmp al,03 ;03: find PCI class code + jnz pci_3j + call pci3class + jmp short pci_3z + +pci_3j: cmp al,06 ;06: generate special cycle + jnz pci_3k + call pci3spec + jmp short pci_3z + +pci_3k: call pci3badc ;others: bad commands + +pci_3z: jb pci_32b ;:error + mov al,0 ;return ok status +pci_32b: mov [ebp._eax+1],al ;status code -> AH + popad ;restore registers + retf + ; + ; bad command + ; +pci3badc: mov al,81h ;return error code + stc + ret + ; + ; 01: PCI BIOS present + ; +pci3pres: mov word [ebp._ebx],pci_ver ;PCI 2.1 -> BX + mov dword [ebp._edx],20494350h ;"PCI " -> EDX + call getlbus ;get number of last PCI bus -> AL + mov [ebp._ecx],al ;-> return in CL + mov byte [ebp._eax],11h ;hardware mechanism 1 -> AL + clc + ret + ; + ; 08: read config byte + ; +pci3readb: mov al,0 ;address mask (forbidden bits) + call pci3idx ;set index + in al,dx ;read data + mov [ebp._ecx],al ;return in CL + ret + ; + ; 09: read config word + ; +pci3readw: mov al,1 ;address mask + call pci3idx ;set index + in ax,dx ;read data + mov [ebp._ecx],ax ;return in CX + ret + ; + ; 0A: read config dword + ; +pci3readd: mov al,03 ;address mask + call pci3idx ;set index + in eax,dx ;read data + mov [ebp._ecx],eax ;return in ECX + ret + ; + ; 0B: write config byte + ; +pci3writb: mov al,00 ;address mask + call pci3idx ;set index + out dx,al ;write data + ret + ; + ; 0C: write config word + ; +pci3writw: mov al,01 ;address mask + call pci3idx ;set index + out dx,ax ;write data + ret + ; + ; 06: generate special cycle + ; +pci3spec: call getlbus ;bus number ok ? + cmp bh,al + ja pci3badc ;:bad + mov bl,0ffh ;device number = FF + mov ecx,edx ;special cycle data -> ECX + xor edi,edi ;register number 0 + ;fall through + ; + ; 0D: write config dword + ; +pci3writd: mov al,03 ;address mask + call pci3idx ;set index + out dx,eax ;write data + ret + ; + ; set PCI configuration index + ; +pci3idx: ror ebx,8 + mov bh,80h + rol ebx,16 + mov dx,[ebp._edi] ;get index + test dl,al ;forbidden bits set ? + jnz pci3idx1 ;:yes + and dh,dh ;high byte ? + jnz pci3idx1 ;:yes + mov bl,dl ;set index + mov dx,pci_ad ;set port address + mov eax,ebx ;get index -> eax + and al,0fch ;mask low bits + out dx,eax ;set index + mov dl,bl ;get low address + or dl,low(pci_dat) ;assume pci_dat $fc (implicit clc) + mov eax,ecx ;write data -> EAX + ret + +pci3idx1: pop eax ;return address + mov al,87h ;bad register number + stc + ret ;return to dispatcher + ; + ; 02: find PCI device + ; +pci3find: inc dx ;vendor ID FFFF ? + jnz pci3find2 ;:ok + mov al,83h ;bad vendor ID + stc + ret + +pci3find2: dec dx ;restore vendor ID + mov ebx,80000000h ;bus address + shl ecx,16 ;device ID -> bits 31..16 + mov cx,dx ;vendor ID -> bits 15..00 + mov edi,0ffffffffh ;mask + jmp short pci3find4 + ; + ; 03: find PCI class code + ; +pci3class: shl ecx,8 ;class code is bits 31..08 + mov edi,0ffffff00h ;mask + mov ebx,80000000h+p_class ;bus address + ; + ; search all buses / devices + ; +pci3find4: mov dx,pci_ad + mov eax,ebx ;device address + mov al,p_id ;vendor / device ID + out dx,eax + mov dl,low(pci_dat) + in eax,dx ;read device / vendor ID + cmp ax,0ffffh ;not present ? + jz pci3find7 ;:skip entire device + cmp bl,0 ;looking for class code ? + jz pci3find5 ;:no + mov dl,low(pci_ad) + mov eax,ebx ;restore register offset + out dx,eax + mov dl,low(pci_dat) + in eax,dx ;read class code +pci3find5: and eax,edi ;mask relevant bits + cmp eax,ecx ;same ? + jnz pci3find6 ;:no + dec si ;device count + js pci3found ;:this is the one + + ; try next function + +pci3find6: test bh,7 ;function 0 ? + jnz pci3find8 ;:no + mov dl,low(pci_ad) + mov eax,ebx ;index + mov al,p_hedt and 0fch ;header type + out dx,eax + mov dl,low(pci_dat) + 2 + in al,dx ;read header type + test al,80h ;multifunction device ? + jnz pci3find8 ;:yes +pci3find7: or bh,7 ;skip the rest of this device + + ; try next device / function + +pci3find8: inc bh ;next device / function + jnz pci3find4 ;:ok + + ; try next bus + + ror ebx,16 + inc bl ;next bus + call getlbus + cmp bl,al + ja pci3find9 ;:not found + ror ebx,16 ;restore + jmp short pci3find4 + + ; didn't find it + +pci3find9: mov al,86h ;device not found + stc + ret + + ; found device + +pci3found: shr ebx,8 ;return bus, device number in BX + mov [ebp._ebx],bx + clc ;ok return + ret + ; + ; 32 bit BIOS entry point + ; +bios_32: and ebx,ebx ;valid function code ? + jz bios_32a ;:ok + mov al,81h ;unimplemented function + retf +bios_32a: cmp eax,49435024h ;$PCI + jz bios_32b ;:ok + mov al,80h ;service is not present + retf +bios_32b: mov al,0 ;ok status + mov ebx,bios3seg shl 16 ;BIOS segment base = $000f 0000 + mov ecx,10000h ;length of BIOS service = 64K + mov edx,pci_32 ;offset from ebx base + retf + use16 + ; + ; 32 bit BIOS header + ; + db (($+15) and 0fff0h)-$ dup 0ffh ;even 16 +bios_32hd db "_32_" + dw bios_32 ;entry point + dw bios3seg ;BIOS segment + db 0 ;revision level 0 + db 1 ;length (16 byte units) + db 0 ;checksum (bios_32hd) - filled in + ;by BIOSSUM.EXE. + db 0,0,0,0,0 ;reserved +#endif diff --git a/PCIPNP.8 b/PCIPNP.8 new file mode 100644 index 0000000..9109f05 --- /dev/null +++ b/PCIPNP.8 @@ -0,0 +1,743 @@ + ; + ; PCI plug & play + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + + ; pd 020716 pci_capa -> set latency timer and cache line size + ; together (16 bit write) + ; pd 011108 set latency timer if P_PRILAT defined + ; pd 000830 add PCI_NORST2 option -> don't touch base register + ; for second motherboard device (e.g. USB) + ; pd 991115 fix PCI I/O allocation: some devices (e.g. ESS Tech) + ; have 16 bit base registers. + ; pd 990329 fix PCI I/O allocation: 4 byte -> 64 bit registers... + ; pd 990216 rewrite I/O allocation + ; pd 980728 change to call postcode routine + ; pd 980728 fix PCI_NORST option -> skip entire device, not just + ; function + + ; + ; To make this BIOS more suitable for future hot plug PCI support, + ; the PCI bus address space allocation has been designed to use + ; minimum granularity and fixed size allocation for bridges, rather + ; than packing things as tightly as possible. This also simplifies + ; the code quite a bit. + ; + ; Limitations: + ; + ; - PCI bridge code not yet tested. + ; - Fixed size allocation for bridges (better to support future + ; hot plug, simpler). As implemented, this does not work for + ; more than 2 levels of bridges. + ; - Except for VGA BIOS, expansion ROMs are not supported. + ; Assume first image, 32 KB size. + ; - Memory allocation below 1MB does not handle memory holes. + ; + ; PCI inherent limitations: + ; + ; - Devices behind bridges don't support memory that must be + ; allocated below 1MB. + ; + ; PCI configuration space structure + ; +p_id equ 0 ;vendor, device ID +p_cmd equ 4 ;command register +p_stat equ 6 ;status register +p_class equ 8 ;class code, revision ID +p_linesz equ 12 ;cache line size +p_lat equ 13 ;latency timer +p_hedt equ 14 ;header type +p_bist equ 15 ;built-in self test +p_base equ 10h ;base address registers +pb_bus equ 18h ;bridge: primary bus number +pb_bus2 equ 19h ;bridge: secondary bus number +pb_bus3 equ 1ah ;bridge: subordinate bus number +pb_lat2 equ 1bh ;bridge: secondary latency timer +pb_io equ 1ch ;bridge: I/O limit +pb_stat equ 1eh ;bridge: secondary status +pb_mem equ 20h ;bridge: memory base low +pb_memp equ 24h ;bridge: prefetchable memory base +p_cis equ 28h ;end of base address registers +pb_mem2 equ 28h ;bridge: memory base high +pb_memp2 equ 2ch ;bridge: prefetchable memory base high +p_rom equ 30h ;expansion rom base +pb_io2 equ 34h ;bridge: I/O base high +pb_rom equ 38h ;bridge: ROM +p_line equ 3ch ;IRQ assigned to function +p_pin equ 3dh ;0 = not used, 1=A, 2=B, 3=C, 4=D +p_mingnt equ 3eh ;minimum grant time +pb_ctl equ 3eh ;bridge: control +p_maxlat equ 3fh ;maximum latency + ; + ; working variables for PNP + ; + ;+ must directly follow previous variable ! + ; + struc tmp_pci + +p_int: dw ?,? ;PCI interrupt lines, LSB = INTA, + ;MSB = INTD +p_irqpt: dw ? ;pointer to IRQ table entry +p_mem: dw ? ;regular memory (64K steps) +p_memlim: dw ? ;+ memory limit +p_memp: dw ? ;prefetchable memory (64K steps) +p_memplim: dw ? ;+ prefetchable memory limit +p_io: dw ? ;I/O address (16 byte steps) +p_iolim: dw ? ;+ I/O limit +p_memr: dw ? ;memory below 1MB (segment value) +p_memrlim: dw ? ;+ limit +p_capa dw ? ;low: 0 if back to back mode supported + ;high: 0 for fast, 2 for medium, 4/6 + ;slow devsel +p_bus: db ? ;current bus +p_lastbus: db ? ;+ last bus number + ends + ; + ; get byte [EBX] -> AL + ; +pci_getb: mov eax,ebx ;set index + mov dx,pci_ad + and al,0fch ;mask low bits + out dx,eax + mov dl,bl ;I/O port index + or dl,0fch ;pci_dat assume $fc + bit mask + in al,dx + ret + ; + ; get word [EBX] -> AX + ; +pci_getw: mov eax,ebx ;set index + mov dx,pci_ad + and al,0fch ;mask low bits + out dx,eax + mov dl,bl ;I/O port index + or dl,0fch ;pci_dat assume $fc + bit mask + in ax,dx + ret + ; + ; get double word [EBX] -> EAX + ; +pci_getd: mov eax,ebx ;set index + mov dx,pci_ad + out dx,eax + mov dl,low(pci_dat) + in eax,dx + ret + ; + ; assign interrupts to device [EBX] + ; +pci_airq: mov bl,p_pin ;which interrupt pin does device use ? + call pci_getb + mov ah,0 + sub al,1 ;0 -> FF + mov si,ax + jb pci_airq3 ;0 -> FF = no interrupt + cmp al,3 + mov al,0ffh + ja pci_airq3 ;out of range -> FF = no interrupt + mov al,[si+p_int] ;get interrupt connected to this line +pci_airq3: mov bl,p_line ;store interrupt number + ;V fall through + ; + ; set byte AL -> [EBX] + ; +pci_setb: push ax ;save data + mov eax,ebx ;get index + mov dx,pci_ad + and al,0fch ;mask low bits + out dx,eax + pop ax ;restore data + mov dl,bl ;I/O port index + or dl,0fch ;assume pci_dat $fc + bit mask + out dx,al + ret + ; + ; set word AX -> [EBX] + ; +pci_setw: push ax ;save data + mov eax,ebx ;get index + mov dx,pci_ad + and al,0fch ;mask low bits + out dx,eax + pop ax ;restore data + mov dl,bl ;I/O port index + or dl,0fch ;assume pci_dat $fc + bit mask + out dx,ax + ret + ; + ; set double word EAX -> [EBX] + ; +pci_setd: xchg eax,ebx ;swap data, index + mov dx,pci_ad + out dx,eax + xchg eax,ebx ;restore data + mov dl,low(pci_dat) + out dx,eax + ret + ; + ; allocate space for a device, CL = ending index + ; (different for normal device / bridge) + ; +pci_dev: mov bl,p_base +pci_dev1: mov eax,0ffffffffh ;find out how many valid bits + call pci_setd ;write + call pci_getd ;and read back + mov ch,al ;save register type + test al,1 ;I/O ? + jz pci_dev1a ;:no + and ax,ax ;high bit set ? (D15) + jns pci_dev9 ;no: this register doesn't work + jmp pci_io ;allocate I/O device + +pci_dev1a: and eax,eax ;high bit set ? (D31) + jns pci_dev9 ;no: this register doesn't work ! + test al,2 ;below 1 MB ? + jnz pci_memr + mov si,p_memp + test al,8 ;prefetchable ? + jnz pci_dev2 ;:yes + mov si,p_mem + + ; allocate memory (normal or prefetchable) + ; + ; we allocate with a minimum of 64KB granularity + +pci_dev2: shr eax,16 ;/ 64KB + not ax ;get requested block size + cmp ax,P_MEMINC-1 ;round up to minimum increment + ja pci_dev3 + mov ax,P_MEMINC-1 +pci_dev3: test [si],ax ;do we need to round up base ? + jz pci_dev4 ;:no + or [si],ax ;round up base + inc word [si] + jz pci_err2 ;:overflow +pci_dev4: inc ax ;size + 1 + add ax,[si] ;update base + jb pci_err2 ;overflow: error + cmp ax,[si+2] ;exceed limit ? + ja pci_err2 ;:yes + xchg ax,[si] ;set new base, get starting base + shl eax,16 ;-> high word, low base is 0 +pci_dev7: call pci_setd ;set base register +pci_dev8: test ch,4 ;64 bit base register ? + jz pci_dev9 ;:32 bit + add bl,4 + xor eax,eax ;clear high register +pci_dev8a: call pci_setd +pci_dev9: add bl,4 + cmp bl,cl ;end of registers ? + jb pci_dev1 ;:try another + ret + +pci_err2: mov al,0c2h ;& error code + jmp pci_err9 + +pci_err3: mov al,0c3h ;& error code + jmp pci_err9 + +pci_err4: mov al,0c4h ;& error code + jmp pci_err9 + ; + ; Allocate <1MB memory space + ; +pci_memr: test al,4 ;reserved type ? + jnz pci_err4 ;:yes + not eax ;find out requested block size + shr eax,4 ;/16 + test eax,0ffff0000h ;error if device wants more than 1MB ! + jnz pci_err3 + cmp ax,P_MEMRINC-1 ;round up to minimum increment + ja pci_memr2 + mov ax,P_MEMRINC-1 + + ;&&& need to add base rounding + +pci_memr2: add ax,[p_memr] ;update base + jb pci_err2 ;overflow: error + cmp ax,[p_memrlim] ;exceed limit ? + ja pci_err2 ;:yes + xchg [p_memr],ax ;get old base, set new + shl eax,4 ;-> get back into place + ;(high EAX is 0 from 1MB test) + jmp short pci_dev7 ;set base register, continue + ; + ; Allocate I/O space + ; + ; Note that I/O is allocated in 256 byte blocks starting at + ; $1000, $1400, etc. (aliases of chipset registers 0000..00FF) + ; + ; Space in x100 .. x3FF is reserved for ISA bus. Space in 0000..00FF + ; and 0480..04FF is reserved for chipset peripherals. 0CF8..0CFF is + ; the PCI config address. So we usually start at 1000. + ; +pci_io: not ax ;find out requested block size + test ax,0ff00h ;> 256 bytes ? + jnz pci_err5 ;:error +;&&& jz pci_io1 ;&&& :ok allocation +;&&& mov ax,00ffh ;&&& allocate 256 bytes +pci_io1: ;&&& + or ax,P_IOINC-1 ;minimum allocation + + test [p_io],ax ;do we need to round up ? + jz pci_ior2 ;:no + or [p_io],ax ;base must be multiple of block size + inc word [p_io] + test word [p_io],0300h ;block overrun ? + jz pci_ior2 ;:no + or word [p_io],03ffh ;next 1024 byte block + inc word [p_io] + jz pci_err5 ;:overflow +pci_ior2: + inc ax ;mask -> count + add ax,[p_io] ;base + block size -> future base + test ax,0300h ;block overrun ? + jz pci_ior3 ;:no + or ax,03ffh ;next 1024 byte block + inc ax + jz pci_err5 ;:overflow +pci_ior3: cmp ax,[p_iolim] ;exceeded limit ? + ja pci_err5 ;:overflow + + xchg [p_io],ax ;get old base, set new + and eax,0ffffh ;clear high half of EAX + jmp pci_dev8a ;set base register, continue + +pci_err5: mov al,bl ;&&& + out post,al + mov al,bh + out post+4,al + jmp pci_err5 ;&&& + + + mov al,0c5h ;& error code + jmp pci_err9 + ; + ; round up [SI], mask CL, set limit using DX if primary bus, + ; DI if secondary bus + ; +pci_rnd: mov ax,[si] + test al,cl + jz pci_rnd1 + or al,cl + inc ax + jz pci_err1 + mov [si],ax +pci_rnd1: cmp byte [p_bus],0 ;primary bus ? + jnz pci_rnd2 + add ax,dx ;primary bus limit + jmp short pci_rnd3 + +pci_rnd2: add ax,di ;secondary bus limit +pci_rnd3: jb pci_err1 ;:too much + mov [si+2],ax ;set limit + ret + +pci_err1: jmp pci_bus9 ;allocation error + ; + ; allocate space for a bridge + ; +pci_bri: mov ax,[p_bus] ;+ lastbus *** pd030520 + push ax ;save initial bus number + inc ah ;get a new bus number + mov [p_lastbus],ah + mov bl,pb_bus ;set primary, secondary bus number + call pci_setw + + mov ax,P_SECLAT * 256 + 0ffh ;subordinate = 255 for config + mov bl,pb_bus3 ;+ pb_lat2 + call pci_setw + + ; save variables for recursion + + mov al,[p_lastbus] ;save old bus number, set new + mov [p_bus],al ;*** pd030520 xchg al,[p_bus] +;*** push ax + push [p_memlim] + push [p_memplim] + push [p_iolim] + + ;bridges don't have fine granularity - start at 1MB / 4K boundaries + + mov si,p_mem ;memory: 1MB boundaries + mov dx,P_MEMINC1 ;primary bus + mov di,P_MEMINC2 ;secondary bus + mov cl,0fh + call pci_rnd + + mov si,p_memp ;prefetch memory: 1MB boundaries + ;mov dx,P_MEMINC1 ;primary bus + ;mov di,P_MEMINC2 ;secondary bus + ;mov cl,0fh + call pci_rnd + + mov si,p_io ;I/O: 4K boundaries + mov cl,0ffh + mov dx,P_IOINC1 ;primary bus + mov di,P_IOINC2 ;secondary bus + call pci_rnd + + ;set bridge base and limit registers + + mov bl,pb_mem ;memory base / limit + mov eax,[p_mem] ;+ p_memlim + sub eax,10000h ;-> inclusive limit + call pci_setd + mov bl,pb_memp ;prefetchable base / limit + mov eax,[p_memp] ;+ p_memplim + sub eax,10000h ;-> inclusive limit + call pci_setd + mov bl,pb_io ;I/O base / limit + mov al,[p_io+1] + mov ah,[p_iolim+1] + dec ah ;-> inclusive limit + call pci_setw + + ;clear high registers + + xor eax,eax ;clear high memory base + mov bl,pb_mem2 + call pci_setd + mov bl,pb_memp2 ;clear high prefetchable base + call pci_setd + mov bl,pb_io2 ;clear high I/O base + call pci_setd + + ; save variables for recursion + + push word [p_capa] + push dword [p_int] + + mov word [p_capa],80h ;bus capabilities + mov bl,pb_stat ;read secondary status + call pci_getw + and byte [p_capa],al ;clear fast back to back if not supportd + or byte [p_capa+1],ah ;set devsel timing + + mov bl,p_cmd ;command register: enable bus master + call pci_getw + or al,04 + mov bl,p_cmd + call pci_setw + + push ebx ;save current EBX + rol dword [p_int],8 ;precompensate for interrupt rotation + + call pci_bus ;enumerate this bus + call pci_capa ;set capability flags + + ; limit -> new allocation pointer + + mov ax,[p_memlim] ;memory + mov [p_mem],ax + + mov ax,[p_memplim] ;prefetchable memory + mov [p_memp],ax + + mov ax,[p_iolim] ;I/O + mov [p_io],ax + + ; restore variables + + pop ebx ;restore after recursion + pop dword [p_int] + pop word [p_capa] + pop word [p_iolim] + pop word [p_memplim] + pop word [p_memlim] + pop ax + mov [p_bus],al ;*** pd030520 was ax, clobbered lastbus + + mov al,[p_lastbus] ;set correct subordinate bus number + mov bl,pb_bus3 + call pci_setb + + mov bl,pb_ctl ;set bridge control register + mov ax,P_BRIDGE + and byte [p_capa],80h ;isolate fast back to back bit + or al,byte [p_capa] ;copy to control register + call pci_setw + ret + ; + ; get next interrupt assignment + ; +pci_nint: test bh,7 ;function 0 ? + jnz pci_nint4 ;no: same interrupts + cmp byte [p_bus],0 ;primary bus ? + jnz pci_nint5 ;:no + cmp word [p_irqpt],pci_tab9 ;end of IRQ table ? + jz pci_nint4 ;yes: don't change + mov si,[p_irqpt] ;get next table entry + cs: lodsd + mov [p_irqpt],si + mov [p_int],eax ;save interrupt setting +pci_nint4: ret + +pci_nint5: ror dword [p_int],8 ;rotate interrupts + ret + ; + ; enumerate a bus - called recursively + ; +pci_bus: mov bh,80h ;enable config access + mov bl,[p_bus] ;bus number + shl ebx,16 ;device, function = 0 + + ; check vendor ID, 0 or FFFF = nothing there, skip device + +pci_bus0: call pci_nint ;get next interrupt assignment + mov bl,p_id ;get vendor / device ID + call pci_getd + inc ax ;FFFF = not present + jz pci_bus4 ;:skip device + dec ax ;0000 = no more functions + jz pci_bus4 ;:skip device + + call pci_airq ;assign interrupts + + call pci_vga ;handle PCI VGA + + ; check header type, different handling if bridge + + mov bl,p_hedt ;get header type + call pci_getb + + and al,7fh + cmp al,1 ;bridge + jz pci_bus1 +#if def PCI_NORST + cmp bh,PCI_NORST ;don't touch base registers on this + jz pci_bus3 ;device (pd 980728: was jz pci_bus2) +#endif +#if def PCI_NORST2 + cmp bh,PCI_NORST2 + jz pci_bus3 +#endif +#if def PCI_NORST3 + cmp bh,PCI_NORST3 + jz pci_bus3 +#endif + mov cl,p_cis ;end index for normal device + call pci_dev ;allocate resources + jmp short pci_bus2 + +pci_bus1: mov cl,pb_bus ;end index for bridge + call pci_dev ;allocate resources + call pci_bri ;enumerate secondary buses + + ; try next function + +pci_bus2: test bh,7 ;function 0 ? + jnz pci_bus4 ;:no, try next subfunction + + ; function 0 - is this a multifunction device + + mov bl,p_hedt ;get header type + call pci_getb + + and al,80h + jnz pci_bus4 ;:yes, multifunction device +pci_bus3: or bh,7 ;step to next device +pci_bus4: inc bh + jnz pci_bus0 + mov ax,[p_mem] ;check allocation limits + cmp ax,[p_memlim] + ja pci_bus9 ;:error + mov ax,[p_memp] + cmp ax,[p_memplim] + ja pci_bus9 ;:error + mov ax,[p_io] + cmp ax,[p_iolim] + ja pci_bus9 + ret + +pci_bus9: mov al,0e5h ;error ! +pci_err9: call postcode ;pd 980728: don't do direct I/O +pci_err99: jmp pci_err99 + ; + ; handle PCI VGA + ; +pci_vga: mov bl,p_class ;read class ID + call pci_getd + shr eax,16 ;check high 16 bits + cmp ax,0300h ;VGA ? + jnz pci_vga9 ;:no + + mov bl,p_cmd ;enable memory access + call pci_getw + push ax + or al,2 + call pci_setw + + mov bl,p_rom ;enable ROM - at P_ROM0 + mov esi,P_ROM0 shl 16 + mov eax,esi + inc ax ;low bit = 1 -> enable + call pci_setd + push ebx ;save ebx context + + call getunreal ;enter unreal mode + + mov ax,[esi] + cmp ax,0aa55h ;ROM header ? + jnz pci_vga2 ;:no + call cs_vshad2 ;copy video BIOS to shadow RAM + ;(chipset specific) + +pci_vga2: pop ebx ;restore ebx + xor eax,eax ;disable ROM + call pci_setd + + pop ax ;restore command register + mov bl,p_cmd + call pci_setw + + xor ax,ax ;restore segment (from unreal mode) + mov ds,ax + mov es,ax +pci_vga9: ret + ; + ; set capability bits & enable devices + ; +pci_capa: xor bx,bx ;start with device / function 0 + mov cx,P_COMMAND ;standard command value + cmp byte [p_capa],80h ;all devices back-to-back capable ? + jnz pci_capa1 ;:no + or ch,02 ;enable fast back-to-back mode +pci_capa1: +#if def PCI_NORST3 + cmp bh,PCI_NORST3 + jz pci_capa1b +#endif +#if def PCI_NORST2 + cmp bh,PCI_NORST2 ;skip this device ? + jnz pci_capa2 ;:no +pci_capa1b: + or bh,7 ;skip device + jmp short pci_capa4 +pci_capa2: +#endif + mov bl,p_id ;get vendor / device ID + call pci_getw + inc ax ;FFFF = not present + jz pci_capa4 ;:skip device + dec ax ;0000 = no more functions + jz pci_capa4 ;:skip device + + mov bl,p_cmd ;read command register + call pci_getw + and ax,0ff5fh ;clear stepping, palette snoop bits + or ax,cx ;enable according to P_COMMAND + call pci_setw ;set command register + + mov bl,p_hedt ;is this a bridge ? &&& pd030520 + call pci_getw + and al,07f ;normal device ? + cmp al,1 ;bridge ? + jz pci_capa3b ;:yes, don't overwrite bus ID + +pci_capa3: + mov bl,p_linesz ;set p_linesz and p_lat + mov al,P_LINSIZE + mov ah,P_PRILAT + call pci_setw ;some devices don't support + ;setting these registers individually +pci_capa3b: + + ; try next function + + test bh,7 ;function 0 ? + jnz pci_capa4 ;:no, try next subfunction + + ; function 0 - is this a multifunction device + + mov bl,p_hedt ;get header type + call pci_getb + +; mov eax,ebx ;device address +; mov al,p_hedt and 0fch ;header type +; mov dx,pci_ad +; out dx,eax +; mov dl,low(pci_dat) + 2 +; in al,dx + and al,80h + jnz pci_capa4 ;:yes, multifunction device + or bh,7 ;step to next device +pci_capa4: inc bh + jnz pci_capa1 + ret + ; + ; PCI plug & play configuration + ; + ; EAX = scratch + ; EBX = 80 / bus / device / function / index + ; CX = scratch + ; DX = port address + ; SI = scratch + ; DI = scratch + ; BP = scratch + ; +pci_pnp: mov word [p_mem],P_MEM0 ;set starting addresses + mov word [p_memp],P_MEMP0 + mov word [p_memr],P_MEMR0 + mov word [p_io],P_IO0 + mov word [p_memlim],P_MEM9 ;set allocation limits + mov word [p_memplim],P_MEMP9 + mov word [p_memrlim],P_MEMR9 + mov word [p_iolim],P_IO9 + ;mov byte [p_bus],0 + ;mov byte [p_lastbus],0 + mov word [p_irqpt],offset pci_tab + + call pci_bus ;enumerate bus + call pci_capa ;set capability flags + + mov al,[p_lastbus] ;set number of last PCI bus + mov byte [cs:d_lastbus],al + ret + ; + ; Disable all devices on primary bus + ; + ; NOTE: Called without stack ! On a chipset with PCI reset + ; function, PCI reset is the easier way... + ; +pci_rst: + +#if def PCI_NOCLR + ret +#else + +#if def PCI_NORST + mov eax,p_cmd+80000800h ;start with device 1 (don't cut off + ;chipset) +#else + mov eax,p_cmd+80000000h ;access bus 0, device 0, function 0 +#endif + +pci_rst1: + +#if def PCI_NORST + cmp ah,PCI_NORST ;don't reset this device + jnz pci_rst2 + add ah,8 +pci_rst2: +#endif + mov dx,pci_ad + out dx,eax ;write index + xchg ax,bx + mov dl,low(pci_dat) + in ax,dx ;read command register + and ax,0fff8h ;disable bus master, I/O, memory access + xchg ax,bx + mov dl,low(pci_ad) + out dx,eax ;write index + xchg ax,bx + mov dl,low(pci_dat) + out dx,ax ;write command register + xchg ax,bx + inc ah ;next device / function + jnz pci_rst1 + ret +#endif + diff --git a/POST.8 b/POST.8 new file mode 100644 index 0000000..5c6a35d --- /dev/null +++ b/POST.8 @@ -0,0 +1,443 @@ + ; + ; POST power on self-test + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; pd 040325 extend temporary area (option TEMPSIZE) + ; pd 021118 add disakbd / enakbd around PS/2 mouse detection + ; (suggested by Klaus Pfaadt) + ; pd 010429 change VGA_END to allow for large VGA BIOS (e.g. + ; C&T / Asiliant VGA) + ; pd 991021 add option for M-Systems DiskOnChip + +#if ! def ROM_BEG +ROM_BEG equ 0c800h ;start of ROM scan +#endif + +#if ! def ROM_END +ROM_END equ 0f800h ;end of ROM scan +#endif + +#if ! def VGA_BEG +VGA_BEG equ 0c000h ;start of VGA BIOS scan +#endif + +#if ! def VGA_END +VGA_END equ 0c800h ;end of VGA BIOS scan +#endif + ; + ; Reset entry + ; + ; Note: Processor shutdown is NOT supported. There are easier and + ; faster ways to get out of protected mode. + ; +reset: cli + cld + mov ax,cs ;SS = CS (to support fake stack) + mov ss,ax + mov ds,ax ;DS = CS (for easier table loads) + mov al,01h ;POST code: reset entry + ret_sp postcode + + ret_sp cs_clr ;clear chipset registers to allow + ;access to DMA, IRQ controller + ret_sp post_clr ;clear registers + ; + ; Initialize chipset + ; +resetcs: mov al,02h ;POST code: chipset initialization + ret_sp postcode + ret_sp cs_init ;initialize chipset +rstini: jb rstvid ;:shadow already enabled + ; + ; Detect base memory size + ; + mov al,03h ;POST code: detect base memory size + ret_sp postcode + ret_sp cs_det ;detect memory +rstdet: ;may return by RET or JMP + ; + ; Init shadow RAM - if DRAM is bad, we'll die here + ; (running out of shadow makes for a more effective + ; memory test, and accelerates startup). + ; + mov al,04h ;POST code: initialize shadow RAM + ret_sp postcode + ret_sp cs_shad ;init shadow +rstshad2: + ; + ; Init Hercules video card (blind init, we don't care if it's there) + ; +rstvid: mov al,05h ;POST code: init mono video + ret_sp postcode +#if ! def NO_VIDINIT + ret_sp vid_init ;let there be light ... +#endif + ; + ; disable all PCI adapters on primary bus -> get bus masters + ; to shut up... + ; +#if def PCI +rstpci: mov al,06h ;POST code: disable PCI devices + ret_sp postcode + ret_sp pci_rst +#endif + ; + ; Check low 64KB of DRAM + ; + mov al,07h ;POST code: test low 64KB of DRAM + ret_sp postcode + ret_sp getunreal ;enter unreal mode + xor ebp,ebp ;start address + mov dx,[m_rstflg] ;save reset flag + ret_sp post_t64k ;test first 64K of DRAM + jnb rstmem5 ;:ok + mov al,0f7h ;POST code: low 64KB failure + jmp fatal ;handle fatal error + +rstmem5: mov [m_rstflg],dx ;restore reset flag + ; + ; initialize stack + ; + mov al,08h ;POST code: initialize stack + ret_sp postcode + mov sp,tmp_stack ;set stack + xor ax,ax + mov ss,ax + ; + ; Set CPU specific parameters, enable L1 cache + ; + call cs_cpu ;DS = 0 + ; + ; Check BIOS checksum + ; + mov al,09h ;POST code: BIOS checksum + call postcode + call cs_shadrw ;set read/write shadow + call d_dosum ;update data checksum + call cs_shadro ;set read only shadow +#if ! def NO_ROMSUM + call post_sum ;verify BIOS checksum + call post_err ;hang if error +#endif + ; + ; Configure super I/O + ; + mov al,0ah ;POST code: super I/O initialization + call postcode + call sio_init + ; + ; Clear RTC interrupts, test shutdown byte, set operating mode + ; + mov al,0bh ;POST code: RTC test + call postcode +#if ! def RTC_SKIP + call rtc_test +#if ! def IGNORE_RTC + call post_err +#endif +#endif + ; + ; Test refresh (and indirectly, 8254 timer) + ; + mov al,0ch ;POST code: refresh / 8254 test + call postcode + call post_ref +#if ! def IGNORE_REF + call post_err +#endif + ; + ; Set speed-dependent chipset registers + ; (done after post_ref) + ; + mov al,0dh ;POST code: speed-dependent chipset regs + call postcode + call cs_spd + ; + ; Test 8237 DMA registers + ; + mov al,0eh ;POST code: test 8237 DMA + call postcode +#if ! def DMA_SKIP + call post_dma + call post_err +#endif + ; + ; Test DMA page registers + ; + mov al,0fh ;POST code: test DMA page registers + call postcode + call post_page + call post_err + ; + ; Test 8254 registers + ; + mov al,10h ;POST code: test 8254 registers + call postcode + call post_tim + call post_err + ; + ; Test keyboard controller (don't care whether we have a keyboard) + ; + mov al,11h ;POST code: test keyboard controller + call postcode + call kb_ini +#if ! def NO_KBC + call post_err +#endif + ; + ; Initialize Timer, DMA, interrupt registers, port 92 + ; + mov al,12h ;POST code: init timer, DMA, 8259... + call postcode + call post_tdma + ; + ; Test 8259 interrupt mask registers + ; + mov al,13h ;POST code: test 8259 mask registers + call postcode + call post_irq + call post_err + ; + ; Size and test low 640 KB + ; + mov al,14h ;POST code: test low 640KB + call postcode + call post_base ;base memory test + ; + ; Initialize memory locations, interrupt vectors, etc. + ; + mov al,15h ;POST code: init vectors + call postcode + call post_vec ;init interrupt vectors + call vid_vars ;initialize Hercules video BIOS vars + mov byte [m_devflg],00110000xb ;no floppy present, monochrome + +#if def GX_VID + call gx_video ;initialize GX video +#endif +#if def GX_INT10 + call gxv_init ;initialize GX int 10 +#endif +#if def VID_CGA + call cs_cga ;enable CGA redirect +#endif + ; + ; run PCI plug & play + ; +#if def PCI + mov al,16h ;POST code: PCI plug & play + call postcode + call cs_shadrw ;enable read / write shadow + +#if def PCI_WAIT + xxx + mov bx,5000 ;option PCI_WAIT: give slow + call cs_waitbx ;PCI peripherals time to wake up +#endif + +#if ! def SKIP_PNP + call pci_pnp + + mov eax,(INTD shl 24)+(INTC shl 16)+(INTB shl 8)+INTA + call cs_pciint ;set interrupt channels +#endif +#endif + ; + ; shadow video BIOS (unless PCI already did it) + ; + mov al,17h ;POST code: shadow video BIOS + call postcode + call cs_vshad + ; + ; Look for VGA video BIOS at C000 + ; + mov al,18h ;POST code: look for VGA BIOS + call postcode + mov bx,VGA_BEG ;starting address + mov dx,VGA_END ;ending address + call post_scan + mov ax,cs + cmp [vec10+2],ax ;did VGA initialize ? + jz rstmda ;:no, still same interrupt + mov byte [m_devflg],0 ;VGA +rstmda: + ; + ; Display signon prompt, base memory size + ; + mov al,19h ;POST code: sign-on prompt + call postcode +#if def CONSOLE + call con_init ;initialize serial console +#endif + mov si,copyrt + call v_msg + xor eax,eax + mov ax,[m_lomem] ;memory size + call post_itoa ;display number + mov si,msg_base ;display " KB Base Memory" + call v_msg + ; + ; keyboard test #2 + ; + mov al,1ah ;POST code: second keyboard test + call postcode + call kb_inb + ; + ; Size and test extended memory + ; + mov al,1bh ;POST code: extended memory test + call postcode +#if ! def SKIP_EXTEST + call cs_a20on ;enable A20 gate + call post_ext ;test extended memory +#endif + ; + ; keyboard test, enable timer tick, enable interrupts + ; + mov al,1ch ;POST code: enable interrupts + call postcode + call kb_inc ;continue keyboard initialization + call tim_init ;initialize timer tick, unmask ints + sti ;enable interrupts + call kb_ind ;set keyboard LEDs + ; + ; test & init RTC + ; + mov al,1dh ;POST code: test / init RTC + call postcode + call rtc_ini + ; + ; Initialize floppy disk: detect, spin up, recalibrate + ; + mov al,1eh ;POST code: init floppy disk + call postcode + call fd_init + ; + ; ROM scan + ; +#if ! def DISKONCHIP + mov al,1fh ;POST code: option ROM scan + call postcode +#if ! def NO_ROMSCAN + mov bx,ROM_BEG ;starting address + mov dx,ROM_END ;ending address + call post_scan +#endif +#endif + ; + ; Test & init parallel ports + ; + mov al,20h ;POST code: test parallel ports + call postcode + call lp_test + ; + ; Test & init serial ports + ; + mov al,21h ;POST code: test serial ports + call postcode + call rs_test + ; + ; enable numeric coprocessor + ; + mov al,22h ;POST code: enable coprocessor + call postcode + call cs_npx + ; + ; secondary floppy init + ; + mov al,23h ;POST code: floppy init + call postcode + call fd_inb + ; + ; IDE initialization + ; + mov al,24h ;POST code: hard disk init + call postcode + call cs_shadrw ;set read/write shadow + call hd_init ;init disk drives + call d_dosum ;update data checksum + call cs_shadro ;set read only shadow + ; + ; Flash disk initialization + ; +#if def FLASHDISK + call fld_init ;initialize flash disk +#endif + ; + ; ROM scan (alternate location for M-Systems DiskOnChip) + ; + ; Caution: their firmware version 3.3.5 will HANG if no HDD + ; present. Connect a HDD, and update their boot image to + ; DOC121.EXB or later. + ; +#if def DISKONCHIP + mov al,1fh ;POST code: option ROM scan + call postcode +#if ! def NO_ROMSCAN + mov bx,ROM_BEG ;starting address + mov dx,ROM_END ;ending address + call post_scan +#endif +#endif + ; + ; detect PS/2 mouse + ; +#if def PS2MOUSE + mov al,25h ;POST code: PS/2 mouse detect + call postcode + call ps2init ;initialize mouse +#endif + ; + ; Timer / RTC update check + ; + ; Note: This test can take up to one second (normally overlapped + ; with floppy / IDE init), skip if not required. + ; + mov al,26h ;POST code: timer/RTC check + call postcode +#if ! def NO_RTCFAIL + call tim_test + cmp byte [tmp_tim],0 +rsttim: jnz rsttim ;hang if failure +#endif + ; + ; enable L2 cache if present + ; +#if def cs_cache + mov al,27h + call postcode + call cs_cache +#endif + ; + ; OEM decision: verify diagnostic flags to decide + ; whether to boot or display error messages + ; + mov al,28h ;POST code: OEM boot decision point + call postcode + call decide + ; + ; clean up before boot + ; + mov word [m_rstflg],0 ;clear reset flag + mov di,0500h ;clear temporary data area: 0500..11FF +#if ! def TEMPSIZE +TEMPSIZE equ 01200 +#endif + mov cx,(TEMPSIZE-0500h) / 2 + xor ax,ax + rep stosw ;this also overwrites stack ! + ; + ; Boot operating system + ; + mov al,00h ;POST code: boot + call postcode +#if def BOOTBEEP + call beep ;let there be noise +#endif + int 19h ;boot + mov al,0dfh ;we shouldn't get here + call postcode + cli + hlt ;hang diff --git a/POST2.8 b/POST2.8 new file mode 100644 index 0000000..b310eef --- /dev/null +++ b/POST2.8 @@ -0,0 +1,655 @@ + ; + ; POST (power on self test) routines + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; Limitations: + ; + ; - Ctrl-Alt-Del does not get any special treatment by POST, goes + ; through full memory test etc. + ; + ; pd050206 change from d_extop to d_exmem + ; pd030516 changed memory test display increment to 16MB + ; (faster when using serial console !) + ; pd011107 add MTEST_1PASS option + ; pd000424 add QUICKMEM option + ; pd991127 add PS/2 mouse vector + + ; + ; handle error: hang if C=1, return otherwise + ; +post_err: jb post_err1 ;:error + ret + +post_err1: cli + hlt ;hang + ; + ; verify BIOS checksum, return C=1 if error + ; +post_sum: mov si,offset startofs ;start offset, must be multiple of 256 + xor bx,bx ;clear sum +post_sum1: cs: lodsw + add bl,al + add bl,ah + cs: lodsw + add bl,al + add bl,ah + and si,si + jnz post_sum1 ;:more to test + and bl,bl ;sum = 0 ? + jnz post_stc1 ;no: error +post_clc1: clc + ret + ; + ; Test memory refresh (and indirectly, 8254 timer) + ; +#if def NO_ISAREF +post_ref: clc ;skip test + ret +post_stc1: stc + ret +#else +post_ref: mov cx,256 +post_ref1: in al,port61 ;wait for refresh bit = 0 + and al,10h + jz post_ref2 + loop post_ref1 +post_stc1: stc ;timeout + ret + +post_ref2: in al,port61 ;wait for refresh bit = 1 + and al,10h + jnz post_clc1 ;yes: return clc + loop post_ref2 + stc ;timeout + ret +#endif + ; + ; Test DMA registers + ; +post_dma: mov dx,dma1 ;I/O port + mov si,2 ;port increment +post_dma1: mov di,dx ;save I/O port + mov bx,8000h ;starting pattern +post_dma2: call post_dma3 ;test + jb post_stc1 ;:error + mov dx,di ;restore I/O port + ror bx,1 ;next pattern + jnb post_dma2 ;:another bit + mov dl,dma0 + shr si,1 ;do dma0 ? + jnb post_dma1 ;:yes + clc ;passed test + ret + +post_dma3: mov cx,8 ;8 address and count registers +post_dma4: mov al,bl ;write test pattern (16 bit = 2 writes) + out dx,al + out iowait,al + mov al,bh + out dx,al + out iowait,al + in al,dx ;read back and compare + cmp al,bl + jnz post_stc1 + out iowait,al + in al,dx + cmp al,bh + jnz post_stc1 + out iowait,al + add dx,si ;next port + loop post_dma4 + clc ;ok return + ret + ; + ; Test IRQ mask registers + ; +post_irq: mov dx,pic0+1 + call post_reg + jb post_irq2 + mov dl,pic1+1 + call post_reg +post_irq2: mov al,0ffh ;mask off all interrupts + out pic0+1,al + out pic1+1,al + ret + ; + ; test register [DX] + ; +post_reg: mov bl,80h ;starting pattern +post_reg2: mov al,bl ;write pattern + out dx,al + not al ;write inverted pattern to prevent + out iowait,al ;capacitive hold + in al,dx ;read back + cmp al,bl + jnz post_stc1 ;:error + out iowait,al + ror bl,1 ;try next bit + jnb post_reg2 + clc ;return ok status +post_reg9: ret + ; + ; Test DMA page registers + ; +post_page: mov dx,fd_page + mov cx,15 ;81..8F +post_pag2: call post_reg + jb post_reg9 ;:error + inc dx + loop post_pag2 + ret + ; + ; Test timer 2 registers + ; +post_tim: mov al,0b0h ;timer 2 + out timer+3,al + out iowait,ax ;this is needed, at least on M6117 ! + out iowait,ax +#if def MEDIAGX + out timer+2,al ;dummy write, read + out timer+2,al + in al,timer+2 + in al,timer+2 +#endif + mov bx,1 +post_tim1: mov al,bl ;LSB + out timer+2,al + out iowait,ax + mov al,bh ;MSB + out timer+2,al + out iowait,ax + in al,timer+2 ;check LSB + cmp al,bl + jnz post_stc2 + out iowait,ax + in al,timer+2 ;check MSB + out iowait,ax + cmp al,bh + jnz post_stc2 + shl bx,1 + jnb post_tim1 + clc + ret +post_stc2: stc + ret + ; + ; Clear registers, disable DMA, interrupts + ; +post_clr: mov si,offset clrtab + jmp short post_tdm0 + ; + ; Initialize timers, DMA + ; +post_tdma: mov si,tdmatab +post_tdm0: mov dh,0 +post_tdm1: cs: lodsw + cmp ah,0ffh ;end of table ? + jz post_tdm9 + mov dl,ah ;port address + out dx,al ;write data + out iowait,al ;I/O wait + jmp post_tdm1 +post_tdm9: ret + ; + ; global descriptor table (GDT) for unreal mode + ; + db (($+15) and 0fff0h)-$ dup 0ffh ;even 16 +gdt: dw gdtend-gdt-1 ;GDT limit +gdtadr: dw gdt,000fh ;linear address of GDT + dw 0 + dw 0ffffh,0,9300h,008fh ;4G data segment, accessed +#if def GX_GDT + dw 0ffffh,0,9300h,408fh ;GX_BASE segment -> GS: +#endif +gdtend: + ; + ; Enter unreal (4GB segment) mode -> change DS,ES selector + ; + ; based on code in DDJ 7/90 + ; +getunreal: cli ;disable interrupts + cs: lgdt [gdt] ;load GDT (in data module, writeable) + + mov eax,cr0 + or al,1 ;enable protected mode + mov cr0,eax + jmp short getunrl2 ;flush queue +getunrl2: mov bx,8 ;selector + mov ds,bx + mov es,bx + and al,0feh ;exit protected mode + mov cr0,eax + ret + ; + ; display high memory size EBP (destroyed) + ; +post_dsph: push ebp ;save EBP +#if def GX_VID + mov ax,0e0bh ;display clear to beginning of line +#else + mov ax,0e0dh ;display CR +#endif + mov bl,0 + int 10h + pop eax ;EBP -> EAX + shr eax,10 ;display high memory size + ;div 1024 -> KB + sub eax,1024 ;sub eax,#1024 + ;fall through + ; + ; display number EAX + ; + ; divide / stack based algorithm + ; +post_itoa: xor dx,dx ;mark first digit on stack + push dx + mov ecx,10 +post_ito2: xor edx,edx + div ecx + or dl,"0" ;remainder -> ASCII digit + push dx ;push digit + and eax,eax ;done ? + jnz post_ito2 + +post_ito3: pop ax ;get digit + and al,al + jz post_ito9 + mov ah,0eh ;TTY output + mov bh,0 ;page 0 + int 10h + jmp post_ito3 + +post_ito9: ret + ; + ; size low memory -> EBX = top address; 64KB granularity + ; (run in unreal mode) + ; +post_szlo: mov ebx,0a0000h ;top limit + xor ecx,ecx ;bottom limit +post_szl0: mov edx,ebx ;save top + mov edi,10000h ;64KB increment + + ; first, write address to memory, counting down + +post_szl1: sub ebx,edi + mov [ebx],ebx + cmp ebx,ecx + jnz post_szl1 + + ; now, verify going up + +post_szl2: add ebx,edi + cmp [ebx],ebx + jnz post_szl3 ;error: done + cmp ebx,edx + jnz post_szl2 +post_szl3: ret ;ebx = top address + ; + ; size high memory + ; (run in unreal mode) + ; + + ; first, we do binary rough size + +post_szhi: xor esi,esi ;start seed + mov [esi],esi + mov ebx,00100000h ;1MB start + mov ecx,ebx ;start for szlo +post_szh1: mov [ebx],ebx + cmp [esi],esi ;wrote over previous ? + jnz post_szh2 ;:yes - reached top + cmp [ebx],ebx ;this location ok ? + jnz post_szh2 ;:no - reached top + mov esi,ebx ;new seed location + shl ebx,1 + test ebx,TOP_MEM shl 16 ;reached top ? + jz post_szh1 ;:not yet +post_szh2: jmp post_szl0 ;ebx is top limit - use low size + ;algorithm now for 64KB resolution + ; + ; Test 64 KB memory block [EBP] + ; (run in unreal mode) + ; + ; preserve EDX ! + ; +post_t64k: mov edi,ebp ;starting address (must be at 64K + ;multiple) + + ; first, do a 64 bit sliding bit test over first 64 x 64 bits + + mov eax,1 ;test pattern + xor ebx,ebx +post_tk1: mov [edi],eax + mov [edi+4],ebx + add di,8 + shl eax,1 + rcl ebx,1 + jnb post_tk1 ;:another bit + + xor di,di ;return to start + mov eax,1 ;test pattern + xor ebx,ebx +post_tk2: cmp [edi],eax + jnz post_tk9 ;:error + cmp [edi+4],ebx + jnz post_tk9 ;:error + add di,8 + shl eax,1 + rcl ebx,1 + jnb post_tk2 + + ; now, write initial test pattern seed + ; 72 bytes long -> always get distance between + ; current cache line and destination + + mov eax,00100100100100100100100100100100xb ;test pattern +post_tk3: mov edi,ebp ;start location + mov di,16 ;skip first 16 bytes + mov esi,edi + + mov cx,18 +post_tk4: a4 stosd + test al,4 ;"round" rotate + jz post_tk41 ;implicit CLC + stc +post_tk41: rcr eax,1 + dec cx + jnz post_tk4 ;:another + + ; do block move -> copies pattern all over memory + + mov ecx,16362 ;word count + a4 rep movsd + + ; now verify final pattern + +post_tk5: cmp eax,[esi] + jnz post_tk9 ;:error + test al,4 ;"round" rotate + jz post_tk51 ;implicit CLC + stc +post_tk51: rcr eax,1 + add si,4 + jnz post_tk5 + + test al,4 ;"round" rotate + jz post_tk52 ;implicit CLC + stc +post_tk52: + +#if !def MTEST_1PASS + rcr eax,1 ;try next pattern + jnb post_tk3 ;:again, total of three passes +#endif + + mov edi,ebp + xor eax,eax + mov cx,16384 + a4 rep stosd ;clear memory + + mov ebp,edi ;new top + clc + ret + +post_tk9: stc ;error return + ret + +#IF DEF QUICKMEM + ; + ; Clear 64 KB memory block [EBP] + ; (run in unreal mode) + ; + ; preserve EDX ! + ; +post_c64k: mov edi,ebp ;starting address (must be at 64K + ;multiple) + xor eax,eax ;zero fill + mov ecx,4000h + a4 rep stosd ;fill + mov ebp,edi ;mov ebp,edi - new top + clc + ret +#ENDIF + ; + ; base memory test + ; +post_base: call getunreal ;enter unreal mode + call post_szlo ;size low memory + mov [tmp_losz],ebx + + mov word [m_lomem],64 ;we have at least 64KB DRAM + mov ebp,10000h ;start address + +post_bas0: +#IF DEF QUICKMEM + call post_c64k ;clear 64K of DRAM +#ELSE + call post_t64k ;test 64K of DRAM +#ENDIF + jb post_bas1 ;:error + add word [m_lomem],64 ;we got another 64KB + cmp ebp,[tmp_losz] + jnz post_bas0 ;:another block + +post_bas1: xor ax,ax ;access BIOS segment + mov ds,ax + mov es,ax + +#if def CM_LEGACY + mov ah,cm_meml ;write base memory size to CMOS + mov al,[m_lomem] + call rtc_write + mov al,[m_lomem+1] + mov ah,cm_memh ;write low memory size + call rtc_write +#endif + ret + ; + ; init interrupt vectors + ; +post_vec: mov si,offset inttab ;init interrupt vectors + xor di,di ;vec00 + mov ax,cs ;BIOS segment -> eax bit 31..16 + shl eax,16 + mov cx,1fh +post_vec1: cs: lodsw ;copy vectors 00.1e + stosd ;vector + segment + loop post_vec1 + add di,4 ;skip vector 1F + + mov cl,60h-20h + mov ax,offset intdummy ;dummy vectors 20..5F + rep stosd + add di,8*4 ;skip 60..67 + mov cl,8 + rep stosd ;fill 68..6F + + mov cl,8 ;copy 8 more vectors +post_vec2: cs: lodsw ;copy vectors 70..77 + stosd + loop post_vec2 + ret + ; + ; Extended memory test + ; +post_ext: call getunreal ;enter unreal mode again (& cli) + + push dword [0] ;save lowest memory - zapped by + ;post_szhi + call post_szhi ;size high memory + pop dword [0] + + mov [tmp_hisz],ebx + + mov ebp,100000h ;start address + +post_ext0: +#IF ! DEF QUICKMEM + test ebp,0ffffffh ;reached 16MB boundary ? + jnz post_ext1 + pushad ;save all registers + call post_dsph ;display current count + call getunreal ;get back to unreal mode + popad ;restore all registers +#ENDIF +post_ext1: +#IF DEF QUICKMEM + call post_c64k ;clear 64K of DRAM +#ELSE + call post_t64k ;test 64K of DRAM +#ENDIF + jb post_ext2 ;:error + cmp ebp,[tmp_hisz] + jnz post_ext0 ;:another block + +post_ext2: push ebp ;save actual top of memory + call post_dsph ;display current count + mov si,msg_ext ;display " KB Extended Memory" + call v_msg + + xor ax,ax ;access BIOS segment + mov ds,ax + mov es,ax + + call cs_shadrw ;shadow read/write + pop eax + push eax + sub eax,0100000 ;subtract base memory + mov dword [cs:d_exmem],eax ;store top address in data area + movzx eax,word [m_lomem] + shl eax,10 ;convert KB into actual size + mov dword [cs:d_basmem],eax + + call cs_shadro + + pop ebx ;store extended memory size in CMOS + shr ebx,10 ;convert to KB + cmp ebx,10000h ;limit to 64MB + jb >l1 + xor bx,bx +l1: sub bx,1024 ;minus base memory + + mov ah,cm_exh ;write high memory size + mov al,bh + call rtc_write + mov al,bl + mov ah,cm_exl ;write low memory size +#if def CM_LEGACY + call rtc_write + mov ah,cm_exh2 ;write high memory size + mov al,bh + call rtc_write + mov al,bl + mov ah,cm_exl2 ;write low memory size +#endif + jmp rtc_write + ; + ; scan for option ROMs + ; +post_scan: mov ds,bx + cmp word [0],0aa55h ;start signature + jnz post_scn8 ;:no + mov cl,[2] ;get length + shl cx,9 ;* 512 + xor si,si ;start offset + xor al,al ;clear sum +post_scn1: add al,[si] ;calculate checksum + inc si + loop post_scn1 + mov word [es:m_ioofs],3 ;offset - call vector + mov [es:m_ioseg],ds ;segment + shr si,4 + add bx,si ;update segment + cmp al,0 + jnz post_scn9 ;:bad checksum + + push bx ;save segment + push dx ;save limit + call far [es:m_ioofs] ;call ROM + cld ;just in case they set it... + pop dx ;restore limit + pop bx ;restore segment + jmp short post_scn9 + +post_scn8: add bx,0080h +post_scn9: cmp bx,dx ;top limit ? + jb post_scan ;:no + xor ax,ax ;restore segments + mov ds,ax + mov es,ax + ret + ; + ; Interrupt vector table + ; +inttab: dw int00 ;divide by zero + dw int01 ;single step + dw nmi ;NMI + dw int03 ;breakpoint + dw int04 ;overflow + dw int05 ;print screen + dw int06 ;invalid opcode + dw int07 ;coprocessor not available + dw irq0 ;IRQ0 system timer + dw irq1 ;IRQ1 keyboard + dw inteoi ;reserved - cascade + dw inteoi ;IRQ3 reserved + dw inteoi ;IRQ4 reserved + dw inteoi ;IRQ5 reserved + dw irq6 ;IRQ6 floppy + dw inteoi ;IRQ7 reserved + dw int10 ;video + dw int11 ;equipment determination + dw int12 ;memory size + dw int13 ;disk + dw int14 ;serial + dw int15 ;system services + dw int16 ;keyboard + dw int17 ;printer + dw int18 ;expansion ROM + dw int19 ;bootstrap + dw int1a ;timer / RTC + dw int1b ;keyboard break + dw int1c ;user timer tick + dw v_parm ;video parameters + dw fd_ptab ;diskette parameters + + dw irq8 ;IRQ8: RTC + dw inteoi2 ;IRQ9: cascade + dw inteoi2 ;IRQ10: spare + dw inteoi2 ;IRQ11: spare +#if def PS2MOUSE + dw irq12 +#else + dw inteoi2 ;IRQ12: spare +#endif + dw irq13 ;IRQ13: spare + dw irq14 ;IRQ14: hard disk +#if def HD_4DRV + dw irq15 ;IRQ15: hard disk +#else + dw inteoi2 ;IRQ15: spare +#endif + ; + ; Display POST code AL + ; + ; This routine is called very early, without a stack. + ; If you decide to display POST codes to a serial or parallel + ; port, you will have to initialize the super I/O first. + ; +#if ! def postcode ;can be overridden by user routine +postcode: out post,al + ret +#endif + ; + ; Display fatal error code AL + ; + ; This routine is called on fatal errors, e.g. bad memory. + ; We just write the POST code, then hang. + ; +#if ! def fatal ;can be overridden by user routine +fatal: out post,al +fatal1: hlt + jmp fatal1 +#endif diff --git a/POSTCODE.TXT b/POSTCODE.TXT new file mode 100644 index 0000000..ce92563 --- /dev/null +++ b/POSTCODE.TXT @@ -0,0 +1,46 @@ + tinyBIOS POST code list pd 980422 + ----------------------- + + 01 reset entry + 02 chipset initialization + 03 detect base memory size + 04 initialize shadow RAM + 05 init mono video + 06 disable PCI devices + 07 test low 64KB of DRAM + 08 initialize stack + 09 BIOS checksum + 0a super I/O initialization + 0b RTC test + 0c refresh / 8254 test + 0d speed-dependent chipset regs + 0e test 8237 DMA + 0f test DMA page registers + 10 test 8254 registers + 11 test keyboard controller + 12 init timer, DMA, 8259... + 13 test 8259 mask registers + 14 test low 640KB + 15 init vectors + 16 PCI plug & play + 17 shadow video BIOS + 18 look for VGA BIOS + 19 sign-on prompt + 1a second keyboard test + 1b extended memory test + 1c enable interrupts + 1d test / init RTC + 1e init floppy disk + 1f option ROM scan + 20 test parallel ports + 21 test serial ports + 22 enable coprocessor + 23 floppy init + 24 hard disk init + 25 PS/2 mouse detect + 26 timer/RTC check + 27 OEM boot decision point + 00 boot + + 33 NMI + F7 low 64KB memory test failed diff --git a/README.TXT b/README.TXT new file mode 100644 index 0000000..8fd2a85 --- /dev/null +++ b/README.TXT @@ -0,0 +1,30 @@ + tinyBIOS read.me Pascal Dornier 071129 + ---------------- pdornier@pcengines.ch + + LICENSING: + + This code is licensed pursuant to the Common Public License rev. 0.5 + (see CPL0_5.HTM). + + Please provide bug fixes or additional features to me for inclusion + in the code base. Contributing to the BIOS core gives you specific + benefits - read the license for details. + + SUPPORT: + + This code is provided "as is", and I cannot provide free support. + Prior versions of tinyBIOS have been used by many customers, with + minimal support required. Contact me for a quote on customer + specific support, adaptations etc. + + INSTALLATION: + + To assemble tinyBIOS, you need to buy the A386 assembler (www.eji.com). + A linker is not required. + + The source code was written using tab stops in columns 11, 19, 41. + The editor built into AS.COM will handle this correctly. See + TOOL\ED.DOC for details on how to use this editor. + + To assemble the WRAP BIOS, go to the WRAP directory and see the + batch files there. diff --git a/RESET.8 b/RESET.8 new file mode 100644 index 0000000..38d2449 --- /dev/null +++ b/RESET.8 @@ -0,0 +1,21 @@ + ; + ; Reset vector + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + safeorg 0fff0h ;at F000:FFF0 + jmp far 0f000h:reset + + dw STARTOFS ;this is used and overwritten by + ;the BIOSSUM utility. + +; The following items are added by the BIOSSUM utility: +; +; db "00/00/00" ;assembly date in mm/dd/yy format +; ;filled in by BIOSSUM utility +; +; db 0ffh ;empty +; db 0fch ;model byte = AT +; db 0 ;checksum goes here - added by BIOSSUM +; ;utility diff --git a/RTC.8 b/RTC.8 new file mode 100644 index 0000000..24e4f4b --- /dev/null +++ b/RTC.8 @@ -0,0 +1,499 @@ + ; + ; Timer / RTC functions + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + + ; + ; Limitations: + ; + ; - Wait function not supported. + ; - Reinitialization doesn't set SET bit. + ; - We don't use CMOS RAM for configuration data + ; (exception: extended memory size). + ; - There is no CMOS checksum. + ; + + ; Year 2000 issue: + ; + ; - Years below 1980 are considered century roll-over, replaced + ; by year 2000. + ; - Please note that DOS will force default date for anything + ; before 1980. + + ; pd 000817 add TICK_RATE option to support AMD Elan SC520 + ; (needs modified tick rate) + ; pd 990211 move tests from cm_shut to cm_test + ; pd 980416 fix alarm function (interrupt mask, CX saved) + + ; + ; INT1A timer BIOS + ; +int1a: +#if def PCI ;transfer to PCI BIOS if necessary + cmp ah,0b1h ;PCI + jnz int1a1 + jmp pci_i1a ;go to PCI BIOS +int1a1: +#endif + sti ;enable interrupts + push ds + push bx + xor bx,bx ;BIOS segment + mov ds,bx + cmp ah,7 + ja int1a_err ;:bad command code + mov bl,ah + shl bx,1 + cli ;disable interrupts + jmp [cs:bx+int1atab] ;dispatch function + ; + ; AH=2: get RTC time + ; +rtc_get: call rtc_uip ;check for RTC update + jb int1a_err ;:error + mov ah,cm_ss ;read seconds -> DH + call rtc_read + mov dh,al + mov ah,cm_mm ;read minutes -> CL + call rtc_read + mov cl,al + mov ah,cm_hh ;read hours -> CH + call rtc_read + mov ch,al + mov ah,cm_b ;read daylight savings bit -> DL + call rtc_read + and al,1 + mov dl,al + jmp short int1a_ok + ; + ; AH=3: set RTC time + ; +rtc_set: push cx + call rtc_uip ;check for RTC update + pop cx + mov ah,cm_ss ;DH -> seconds + mov al,dh + call rtc_write + mov ah,cm_mm ;CL -> minutes + mov al,cl + call rtc_write + mov ah,cm_hh ;CH -> hours + mov al,ch + call rtc_write + mov ah,cm_b ;read status register B + call rtc_read + and al,01100010xb ;mask off set, update interrupt, + ;square wave, daylight savings + or al,2 ;set 24 hour mode + or al,dl ;add daylight savings bit from DL + call rtc_write ;update status register B +int1a_ok: clc +int1a_ret: sti + pop bx + pop ds + retf 2 + +int1a_err: stc + jmp short int1a_ret + ; + ; AH=4: get RTC date + ; +rtc_date: call rtc_uip ;check for RTC update + jb int1a_err + mov ah,cm_dd ;day -> DL + call rtc_read + mov dl,al + mov ah,cm_mo ;month -> DH + call rtc_read + mov dh,al + mov ah,cm_yy ;year -> CL + call rtc_read + mov cl,al + mov ah,cm_cent ;century -> CH + call rtc_read + mov ch,al + cmp cx,1980h ;century roll-over ? + jae rtc_date9 + mov ax,cm_cent*256+20h ;update century register + call rtc_write + mov ch,20h ;force 2000 +rtc_date9: jmp int1a_ok + ; + ; AH=5: set RTC date + ; +rtc_sdat: push cx + call rtc_uip ;check for RTC update + pop cx + mov ax,cm_day*256 ;0 -> day of week (not used) + call rtc_write + mov ah,cm_dd ;DL -> day + mov al,dl + call rtc_write + mov ah,cm_mo ;DH -> month + mov al,dh + call rtc_write + mov ah,cm_yy ;CL -> year + mov al,cl + call rtc_write + mov ah,cm_cent ;CH -> century + mov al,ch + call rtc_write + jmp int1a_ok + ; + ; AH=6: set RTC alarm + ; +rtc_alrm: mov ah,cm_b ;read status B + call rtc_read + and al,20h ;alarm enabled ? + jnz int1a_err ;:error + push cx ;save CX ! + call rtc_uip ;check for RTC update + pop cx ;restore... + mov ah,cm_ssa ;DH -> alarm second + mov al,dh + call rtc_write + mov ah,cm_mma ;CL -> alarm minute + mov al,cl + call rtc_write + mov ah,cm_hha ;CH -> alarm hour + mov al,ch + call rtc_write + + in al,pic1+1 ;read mask register + and al,0feh ;enable RTC interrupt (8) + out pic1+1,al + + mov ah,cm_b ;read status B + call rtc_read + or al,20h ;enable alarm + call rtc_write + jmp int1a_ok + ; + ; AH=7: clear RTC alarm + ; +rtc_snz: mov ah,cm_b ;read status B + call rtc_read + and al,5fh ;disable alarm interrupt + call rtc_write + jmp int1a_ok + ; + ; AH=0: get time + ; +tm_get: mov dx,[m_timer] ;DX = timer low + mov cx,[m_timer+2] ;CX = timer high + mov al,0 + xchg al,[m_timofl] ;AL = timer overflow, reset flag + jmp int1a_ok + ; + ; AH=1: set time + ; +tm_set: mov byte [m_timofl],0 ;clear timer overflow flag + mov [m_timer],dx ;DX = timer low + mov [m_timer+2],cx ;CX = timer high + jmp int1a_ok + ; + ; INT1A dispatch table + ; + even +int1atab: dw tm_get ;AH=0: get time + dw tm_set ;AH=1: set time + dw rtc_get ;AH=2: get RTC time + dw rtc_set ;AH=3: set RTC time + dw rtc_date ;AH=4: get RTC date + dw rtc_sdat ;AH=5: set RTC date + dw rtc_alrm ;AH=6: set RTC alarm + dw rtc_snz ;AH=7: clear RTC alarm + ; + ; Clear RTC interrupt, test shutdown byte + ; + ; Be sure to leave cm_test non-zero, as this is used by the + ; master reset code in cs_clr to determine whether to reset + ; the bus. + ; +rtc_test: mov al,cm_nmi+cm_c ;clear pending interrupt + out cm_idx,al + out iowait,al + in al,cm_dat + + mov bx,8000h+cm_nmi+cm_test ;bit to test, test register +rtc_test1: mov al,bl ;write pattern + out cm_idx,al + out iowait,al + mov al,bh + out cm_dat,al + out iowait,al + + mov al,bl ;read back + out cm_idx,al + out iowait,al + in al,cm_dat + out iowait,al + cmp al,bh + jnz rtc_test8 ;:error - clc -> inverted -> error + shr bh,1 ;shift pattern right + jnb rtc_test1 ;:another bit +rtc_test8: cmc ;last bit inverted -> no carry if ok + ret + ; + ; Wait for RTC UIP bit cleared + ; + ; This bit is set about 250ęs before the next update. If clear, + ; we have at least 250ęs to read or write the RTC without + ; updates coming in between. + ; +rtc_uip: mov cx,1000 + mov ah,cm_a +rtc_uip1: cli + call rtc_read + and al,80h ;UIP ? + jz rtc_uip9 ;:ok, carry clear + sti ;give interrupts a chance + loop rtc_uip1 ;:try again + mov ax,cm_a*256+26h ;set 32768 Hz oscillator, 1 ms int + call rtc_write ;to restart... + stc +rtc_uip9: ret + ; + ; read RTC register [AH] -> AL + ; +rtc_read: mov al,ah + out cm_idx,al + out iowait,al + in al,cm_dat + out iowait,al + ret + ; + ; write RTC register AL -> [AH] + ; +rtc_write: xchg al,ah + out cm_idx,al + out iowait,al + xchg al,ah + out cm_dat,al + ret + ; + ; clock tick (IRQ0) + ; +irq0: sti ;enable interrupts + push ax + push dx + push ds + +#if def debug + call diag_csip ;debug: display CS:IP on MDA +#endif + xor ax,ax ;access BIOS segment + mov ds,ax + + mov ax,[m_timer] ;update timer + mov dx,[m_timer+2] + add ax,1 + adc dx,0 + cmp ax,00b2h ;24 hours ? + jnz irq0_1 + cmp dx,0018h + jnz irq0_1 + xor ax,ax ;timer overflow - back to 0 + xor dx,dx + mov byte [m_timofl],1 +irq0_1: mov [m_timer],ax + mov [m_timer+2],dx + + dec byte [m_fdcnt] ;floppy motor timer + jnz irq0_2 ;:not yet + mov al,0ch + mov dx,fdc_ctrl + out dx,al ;turn off motor + and byte [m_fdmot],0f0h ;turn off motor bits + +irq0_2: int 1ch ;call user hook + + pop ds + pop dx + mov al,eoi ;signal end of interrupt + cli + out pic0,al + pop ax + iret + ; + ; RTC interrupt (IRQ8) + ; +irq8: push ax + mov al,cm_c ;check alarm interrupt bit + out cm_idx,al + out iowait,al + in al,cm_dat + test al,20h + jz irq8_1 + push ax + int 4ah ;call user hook + pop ax +irq8_1: mov al,eoi ;signal end of interrupt + out pic1,al + out pic0,al + pop ax + iret + ; + ; Timer initialization -> 18.2 Hz tick + ; Unmask timer and keyboard interrupts + ; +tim_init: mov al,36h ;square wave generator + out timer+3,al +#if def TICK_RATE + mov al,low(TICK_RATE) ;LSB + out timer,al + mov al,high(TICK_RATE) ;MSB + out timer,al +#else + mov al,0 + out timer,al + out timer,al +#endif + + in al,pic0+1 ;enable timer, keyboard interrupt + and al,11111100xb + out iowait,al + out pic0+1,al + out iowait,al + mov al,eoi + out pic0,al + ret + ; + ; RTC init + ; +rtc_ini: mov ah,cm_d ;read status register D + call rtc_read + and al,80h ;battery low ? + jz rtc_ini0 ;:yes + + ; battery ok - validate the time / date + + mov ah,2 ;get RTC time + int 1ah + jb rtc_ini0 ;:error + mov al,dh ;validate seconds + mov ah,60h + call rtc_val + jb rtc_ini0 ;:bad + mov al,cl ;validate minutes + mov ah,60h + call rtc_val + jb rtc_ini0 ;:bad + mov al,ch ;validate hours + mov ah,24h + call rtc_val + jb rtc_ini0 ;:bad + + mov ah,4 ;get RTC date + int 1ah + mov al,dl ;day + mov ah,31h + call rtc_val + jb rtc_ini0 ;:bad + mov al,dh ;month + mov ah,12h + call rtc_val + jb rtc_ini0 ;:bad + mov ax,cx + cmp ax,1980h ;minimum 1980 + jb rtc_ini0 + cmp ax,2099h ;maximum 2099 + ja rtc_ini0 + mov ah,99h ;maximum year + call rtc_val + jb rtc_ini0 ;:bad + + mov ax,cm_dia*256 ;clear diag register + call rtc_write + jmp short rtc_ini2 + + ; battery was low or invalid time - initialize the RTC + +rtc_ini0: inc byte [tmp_rtc] ;set RTC failure flag + mov si,offset rtc_tab +rtc_ini1: cs: lodsw ;get value from table + call rtc_write ;write to RTC + cmp si,offset rtc_tab9 ;end of table ? + jb rtc_ini1 ;:no + + ; Set timer tick value from RTC time + ; + ; Please note that there are different algorithms with varying + ; accuracy for doing this, there can be slight time discrepancies + ; depending on what algorithm is used by the OS. + +rtc_ini2: mov ah,2 ;get RTC time + int 1ah + jb rtc_ini9 ;:error + mov [tmp_ss],dh ;save second for run check + mov [tmp_mm],cl ;save minute for run check + push dx + xor ebx,ebx + mov al,ch ;hour + mov edx,01000755h ;18.206 * 3600 * 256 + call rtc_mul + mov al,cl ;minute + mov edx,00044464h ;18.206 * 60 * 256 + call rtc_mul + pop ax ;second + mov al,ah + mov dx,4661 ;18.206 * 256 + call rtc_mul + shr ebx,8 ;timer / 256 + mov dword [m_timer],ebx ;set timer + mov [tmp_timer],bx ;backup for run check +rtc_ini9: ret + ; + ; convert number in AL from BCD -> binary, * EDX -> add to EBX + ; +rtc_mul: mov ah,al ;high digit + and al,15 ;mask low digit + shr ah,4 ;high -> low nibble + aad ;convert to binary + db 066h + cbw ;clear top of eax + mul edx + add ebx,eax ;add ebx,eax + ret + ; + ; validate a BCD number in AL, AH = limit + ; +rtc_val: cmp al,ah ;exceed limit ? + ja rtc_val9 ;(no carry -> cmc -> error) + and al,15 ;high digit is ok, now check low digit + cmp al,10 ;(less than 10 -> carry -> cmc -> ok) +rtc_val9: cmc ;return error status + ret + ; + ; Timer & RTC test + ; +tim_test: mov ax,[m_timer] ;did we get at least one timer tick ? + cmp ax,[tmp_timer] + jnz tim_test0 ;:ok + + ; Could fail if floppy and IDE init were super fast. Give it + ; another chance. + + mov bx,60 ;wait 60 ms + call cs_waitbx + mov ax,[m_timer] ;did we get at least one timer tick ? + cmp ax,[tmp_timer] + jz tim_test8 ;no: error + +tim_test0: add ax,20 ;wait max. of one second + xchg ax,bx +tim_test1: mov ah,2 ;read RTC + int 1ah + jb tim_test8 ;:error + cmp dh,[tmp_ss] ;compare second + jnz tim_test9 ;:ok + cmp cl,[tmp_mm] ;compare minute + jnz tim_test9 ;:ok + cmp bx,[m_timer] ;time-out ? + js tim_test8 ;:yes + hlt ;wait for next interrupt + jmp tim_test1 ;look again + +tim_test8: inc byte [tmp_tim] ;set error flag +tim_test9: ret diff --git a/TABLES.8 b/TABLES.8 new file mode 100644 index 0000000..d9d2b38 --- /dev/null +++ b/TABLES.8 @@ -0,0 +1,110 @@ + ; + ; initialization tables + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + + ; + ; Initial initialization table (used in post2) + ; +clrtab: db cm_nmi+cm_b,cm_idx ;RTC: disable NMI + db 2,cm_dat ;RTC: BCD mode, interrupt disable + db cm_nmi+cm_a,cm_idx ;RTC: 32768 Hz oscillator, 1ms int + db 26h,cm_dat + + db 4,dma0+8 ;disable dma0 + db 4,dma1+8+8 ;disable dma1 + db 0,dma0+0dh ;reset dma0 (data don't care) + db 0,dma1+0dh+0dh ;reset dma1 (data don't care) + db 0fh,dma0+0fh ;mask all channels + db 0fh,dma1+0fh+0fh ;mask all channels + db 0,dma0+8 ;Enable dma0 + db 0,dma1+8+8 ;Enable dma1 + + db 0fch,port61 ;disable parity + +#if def NO_ISAREF + db 50h,timer+3 +#else + db 54h,timer+3 ;init refresh +#endif + db 12h,timer+1 ;refresh interval + + db 0ffh,0ffh ;end of table + ; + ; Timer, DMA, PIC initialization table (used in post2) + ; + ; first data byte, then port address + ; +tdmatab: db 0,dma1+8+8 ;Enable dma1 + db 0c0h,dma1+0bh+0bh ;Cascade mode, channel 4 + db 041h,dma1+0bh+0bh ;Single mode, channel 5 + db 042h,dma1+0bh+0bh ;Single mode, channel 6 + db 043h,dma1+0bh+0bh ;Single mode, channel 7 + db 0,dma1+09h+09h ;clear DRQ4 request + db 0,dma1+0ah+0ah ;unmask DRQ4 -> enable cascade + db 0,dma0+8 ;Enable dma0 + db 40h,dma0+0bh ;Single mode, channel 0 + db 41h,dma0+0bh ;Single mode, channel 1 + db 42h,dma0+0bh ;Single mode, channel 2 + db 43h,dma0+0bh ;Single mode, channel 3 + + db 0,npx+1 ;clear x87 interrupt + + db 11h,pic0 ;ICW1: edge, ICW4 + db 08h,pic0+1 ;ICW2: interrupt vector 08..0F + db 04h,pic0+1 ;ICW3: IRQ2 is used for cascade + db 01h,pic0+1 ;ICW4: 8086 mode + db 0ffh,pic0+1 ;OCW: mask all interrupts + + db 11h,pic1 ;ICW1: edge, ICW4 + db 70h,pic1+1 ;ICW2: interrupt vector 70..77 + db 02h,pic1+1 ;ICW3: slave identification number + db 01h,pic1+1 ;ICW4: 8086 mode + db 0ffh,pic1+1 ;OCW: mask all interrupts + + db 26h,port92 ;enable A20 gate + + db 0ffh,0ffh ;end of table + ; + ; baud rates, H/L swapped + ; +#if ! def rs_baud ;can be overridden by chipset +rs_baud: db 04,17h ;110 baud + db 03,00h ;150 baud + db 01,80h ;300 baud + db 00,0c0h ;600 baud + db 00,60h ;1200 baud + db 00,30h ;2400 baud + db 00,18h ;4800 baud + db 00,0ch ;9600 baud +#endif + ; + ; serial port I/O addresses (points to scratch register) + ; +rs_ports: dw 3ffh,2ffh,3efh,2efh,287h,28fh + dw 297h,29fh,2F7h,377h,0 ;0 = end of table + ; + ; parallel port I/O addresses + ; +lp_ports: dw 03bch,0378h,0278h,0 ;0 = end of table + ; + ; RTC initialization table + ; + ; Note: When location cm_dia is set to 80, MS-DOS will ignore the + ; RTC date and set 1980. + ; +rtc_tab: db 0,cm_ss ;00: second + db 0,cm_ssa ;01: alarm second + db 0,cm_mm ;02: minute + db 0,cm_mma ;03: alarm minute + db 0,cm_hh ;04: hour + db 0,cm_hha ;05: alarm hour + db 0,cm_day ;06: day of week + db 1,cm_dd ;07: day + db 1,cm_mo ;08: month + db 00h,cm_yy ;09: year + db 80h,cm_dia ;0E: power loss flag + db 20h,cm_cent ;32: century +rtc_tab9: ;end of table diff --git a/TOOL/ADD.COM b/TOOL/ADD.COM new file mode 100644 index 0000000..7493b31 Binary files /dev/null and b/TOOL/ADD.COM differ diff --git a/TOOL/AS.COM b/TOOL/AS.COM new file mode 100644 index 0000000..79b9a82 Binary files /dev/null and b/TOOL/AS.COM differ diff --git a/TOOL/BIOSSUM.EXE b/TOOL/BIOSSUM.EXE new file mode 100644 index 0000000..e1b29b1 Binary files /dev/null and b/TOOL/BIOSSUM.EXE differ diff --git a/TOOL/BIOSSUM.PAS b/TOOL/BIOSSUM.PAS new file mode 100644 index 0000000..e451090 --- /dev/null +++ b/TOOL/BIOSSUM.PAS @@ -0,0 +1,186 @@ +{$r-,i-,s-} + +{ BIOS checksum utility + + (C)2001 Pascal Dornier / PC Engines + This file is licensed pursuant to the COMMON PUBLIC LICENSE Rev. 0.5. + + - find _32_ (paragraph aligned), insert checksum byte for 32 bit + PCI BIOS header + + - find _DAT (word aligned), insert checksum byte for BIOS read/write + data area + + - insert BIOS date + + - insert model byte + + - insert overall checksum byte + + Compile using Borland Pascal 7.0 (older versions likely to work too) +} + +uses dos; + +const + zero=ord('0'); + hx:array[0..15] of char = '0123456789ABCDEF'; + +type + bios=array[0..$fffe] of byte; + +var + b:^bios; + fi:file; + blen:word; + startofs,datofs,pciofs:word; + i:word; + sum:byte; + yy,mm,dd,dw:word; + +procedure hexw(i:word); +begin + write(hx[i shr 12],hx[hi(i) and 15],hx[lo(i) shr 4],hx[i and 15]); +end; + +begin + if paramcount<>2 then begin + writeln('BIOSSUM usage: BIOSSUM infile outfile'); + halt(1); + end; + + { read input file } + + new(b); + assign(fi,paramstr(1)); + reset(fi,1); + if ioresult<>0 then begin + write('Could not open input file ',paramstr(1)); + halt(1); + end; + blockread(fi,b^,sizeof(b^),blen); + close(fi); + + if blen<>$fff7 then writeln('Warning: input file length incorrect ?'); + + { get start offset } + + startofs:=b^[$fff5]+swap(b^[$fff6]); { stored just after the reset vector } + + { find _32_ (paragraph aligned), insert checksum byte for 32 bit + PCI BIOS header } + + asm + xor bx,bx { clear offset } + les di,b { pointer to BIOS buffer } + mov di,startofs + db $66 { mov eax,"_32" } + mov ax,$335f + dw $5f32 +@l1: db $66 { cmp [di],eax } + cmp [es:di],ax + jz @l2 { :found header } + add di,16 { next paragraph } + jnz @l1 { :keep looking } + jmp @l9 { not found } + +@l2: mov bx,di { remember start address } + xor al,al { calculate checksum } + mov cx,10 +@l3: add al,[es:di] + inc di + loop @l3 + neg al + stosb + +@l9: mov pciofs,bx { return offset, 0 if not found } + end; + if pciofs=0 then + writeln('_32_ area not found.') + else begin + write('_32_ area found at '); hexw(pciofs); writeln; + end; + + { find _DAT (word aligned), insert checksum byte for BIOS read/write + data area } + + asm + xor bx,bx { clear offset } + les di,b { pointer to BIOS buffer } + mov di,startofs + mov cx,di + neg cx + shr cx,1 + mov ax,$445F { _D } +@l1: jcxz @l9 { bail if nothing left } + repnz scasw + jnz @l9 { :not found } + cmp word [es:di],$5441 { AT ? } + jnz @l1 { :keep searching } + lea bx,[di-2] { save offset } + + mov cx,[es:di+2] { get byte count } + int 3 {&&&} + add di,4 + xor al,al +@l2: add al,[es:di] { calculate checksum } + inc di + loop @l2 + neg al { store checksum } + stosb + +@l9: mov datofs,bx { return offset, 0 if not found } + end; + if datofs=0 then + writeln('_DAT area not found.') + else begin + write('_DAT area found at '); hexw(datofs); writeln; + end; + + { insert BIOS date } + + getdate(yy,mm,dd,dw); + b^[$fff5]:=(mm div 10)+zero; + b^[$fff6]:=(mm mod 10)+zero; + b^[$fff7]:=ord('/'); + b^[$fff8]:=(dd div 10)+zero; + b^[$fff9]:=(dd mod 10)+zero; + b^[$fffa]:=ord('/'); + b^[$fffb]:=((yy mod 100)div 10)+zero; + b^[$fffc]:=(yy mod 10)+zero; + b^[$fffd]:=$ff; + + { insert AT model byte } + + b^[$fffe]:=$fc; + + { calculate overall checksum } + + sum:=0; + for i:=startofs to $fffe do + inc(sum,b^[i]); + + i:=$ffff; + b^[i]:=-sum; + + { write output file } + + assign(fi,paramstr(2)); + rewrite(fi,1); + if ioresult<>0 then begin + write('Could not create output file ',paramstr(1)); + halt(1); + end; + + if startofs=0 then begin + blockwrite(fi,b^[0],$8000); { write 64KB in two pieces } + blockwrite(fi,b^[$8000],$8000); + end else + blockwrite(fi,b^[startofs],$10000-startofs); + + if ioresult<>0 then begin + write('Write error !'); + halt(1); + end; + close(fi); +end. diff --git a/TOOL/CAT.COM b/TOOL/CAT.COM new file mode 100644 index 0000000..c4058c6 Binary files /dev/null and b/TOOL/CAT.COM differ diff --git a/TOOL/ED.COM b/TOOL/ED.COM new file mode 100644 index 0000000..8f47538 Binary files /dev/null and b/TOOL/ED.COM differ diff --git a/TOOL/ED.TXT b/TOOL/ED.TXT new file mode 100644 index 0000000..75363c9 --- /dev/null +++ b/TOOL/ED.TXT @@ -0,0 +1,231 @@ +Calling the editor +------------------ + +Call ED.COM using + + ED [-m=macfile] [file] + +File is the (optional) name of the file to be edited. If this file is invalid +or doesn't exist yet, "Disk error" is indicated. If the -m=macfile option is +specified (macfile is the name of the keyboard macro file), the macro file is +read on startup. + +On errors the editor expects you to hit the space bar as an acknowledgement. + +Some control characters can modify video attributes (if the editor is installed +properly for your screen). Insert the chars using the ^P command: + +^B Boldface on +^U Underline on +^N Normal (bold and underline off) + +Hard tabs are expanded. As usual, tab positions are at column 9, 17, 25 etc. +The end of the file is displayed by a gray area. + +Editor commands +--------------- + +For many commands function keys can also be used. + +^A word left +^C page down +^D character right +^E line up +^F word right +^G delete char right +^H backspace, delete left +^I tabulator (insert a hard tab) +^J jump to symbol = search + Enter word to be searched. The search is case-sensitive. + Cursor jumps to first instance in text (search next: ^L) +^K prefix for block, file and assembler commands +^L repeat previous search (& replace) +^N insert line break +^O prefix for macro commands +^P prefix for entering control chars + (to enter ^_ type ^_^P^_) +^Q prefix for jump commands +^R page up +^S character left +^T delete word right +^U cursor line -> middle of the screen +^V toggle insert / overwrite +^W scroll up +^X line down +^Y delete line +^Z scroll down +ESC exit editor - also see file management. + +Block operations +---------------- + +All block operations are line-oriented, i.e. they always treat entire lines. + +^KA block = all (entire text) +^KB mark block beg +^KC copy block +^KE mark block end +^KJ append (join) block to file (ignores ^Z) +^KK mark block end +^KN block = nothing +^KR read block +^KV move block +^KW write block +^KY delete block + +Quick jump commands +------------------- + +^QA Search and replace. Asks for options. Valid options are: + + g = global (start from beginning) + n = don't prompt for replacement + u = don't distinguish upper and lower case + w = search whole words only + + Valid answers to the prompt are: + + y = ok, do replace + n = don't replace this one + * = go ahead, don't ask me any more + Esc = stop it + +^QB jump to block beg +^QC jump to text end +^QD jump to line end +^QE jump to block end +^QF search (same options as ^QA) +^QI toggle auto-indent option +^QK jump to block end +^QL undo changes in current line +^QR jump to text beg +^QS jump to line beg +^QY delete to end of line +^QZ delete to beg of line + +File management +--------------- + +When calling ED from DOS a main file may be specified on the command line which +is then read into the text buffer. + +Files are written back without query (there is a special command to throw away +changes made to the file). ED does not make .BAK files (the MS-DOS file system +is too damn slow). + +The editor can quickly switch between two files: main and include. Both files +must fit into the text buffer (about 64 K) together. + +Esc Exit ED + - from main: save main, return to DOS + - from incl: save incl, return to main + +^KI Back to the previous include file (name displayed in status line) + +^KL Load/change include file + - from main: load new include file + - from incl: save incl, load new include file + +^KM Load/change main file + - from main: save main, load new main file + - from incl: save incl, back to main + +^KQ Throw away changes (query) + - from main: don't save main, return to DOS + - from incl: don't save incl, return to main + +^KS Save current file (manual save) + +Should it be impossible to save a file the usual way (disk full etc.), mark the +text with ^KA, write it with ^KW to another disk and exit ED with ^KQ. If you +are prompted for a save filename, but want to throw away the file, enter 'nul' +as filename. DOS will dutifully write the file to the null device. + +Function keys +------------- + +Function keys can be used for some commands: + +up cursor up +down cursor down +left cursor left ^left word left +right cursor right ^right word right +PgUp page up ^PgUp mark block beg +PgDn page down ^PgDn mark block end +Home jump to line beg ^Home jump to text beg +End jump to line end ^End jump to text end + +Ins toggle insert/overwrite mode +Del delete char right +^BS delete to end of line + +ScrLock Lock cursor line. The cursor will always remain in the same line of the + screen (as far as possible). This is nice for bulk revisions. The + cursor line can be moved with ^W, ^Z and ^U. + +Keyboard Macros +--------------- + +The following keys can be programmed with macros of (nearly) any length (all +together up to 868 bytes, where each keystroke takes 1..2 bytes). Which keys +can be used may also depend on your keyboard driver. + + F1..F10 + Shift F1..F10 + Ctrl F1..F10 + Alt F1..F10 + Alt A..Z + Alt 1..9,0,-,= (all keys in the number row) + +^OL Record keyboard macro + + Hit the destination key, then enter the command sequence (which is + executed as usual, so you have visual feedback). Terminate the sequence + with ^Break. Mistakes will also be recorded faithfully and cannot be + edited except by recording the macro again. + + Macros can also be used during recording. Nesting is limited to + 15 levels, everything beyond is ignored. A macro that calls itself + once will repeat 16 times, if it calls itself twice it will repeat + 2^16 times... Macros may be interrupted by ^Break. + +^_ This suspends macro execution and lets the user enter data (no macros) + terminated by ; macro execution will then resume. The user's + is ignored: The macro may edit or extend the entry (e.g. add a + default extension to a filename). + +^OW Write macros to file + +^OR Read macros from file. This automatically executes the Alt-0 macro, + making it possible to "chain" macros. + +^OV Update the display. Normally, the display is not updated during macro + execution (resulting in fast execution of macros). This command can be + used to show intermediate results. + +Status line +----------- + +The editor status is displayed in the top line of the screen: + + ^K no 3 main + include filename + +^K Command prefix. Displayed while entering commands such as ^KB. + +n Indicates automatic indentation switched off. Toggle with ^QI. + +o Indicates overwrite mode. Toggle with ^V or INS. + +3 Indicates cursor position in line. + +Editing in the status line is similar to normal editing, but: + +- Trailing spaces are not cut off. +- ^A jumps to beg of line +- ^F jumps to end of line +- Esc breaks the command. +- When a default is given, if the first key pressed is a char, + the default is cleared and the char is entered. If the first + key is an editor command, the default can be edited. To insert + something at the beginning of the default, first hit the + cursor left key. diff --git a/VID.8 b/VID.8 new file mode 100644 index 0000000..9d5407c --- /dev/null +++ b/VID.8 @@ -0,0 +1,508 @@ + ; + ; Hercules monochrome video BIOS + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; Limitations: + ; + ; - No CGA / graphics support - CGA cards / monitors are no longer + ; available. VGA is handled by separate BIOS anyway, Hercules + ; support is mainly for debug purposes. + ; - No light pen support + ; - No print screen function (risky for embedded systems) + ; - Video mode ignored (monochrome only supports 80x25) + ; - INT 1D (pointer to video parameters) not supported. + ; - Font not supported. + ; - Historic entry points not supported. + ; - INT 1F extended font not supported. + ; - AH=13 Write string not supported (AT only) + ; + + ; + ; Initialize text mode + ; +vid_init: mov al,21h ;disable video, high res bit + mov dx,crtc+4 + out dx,al + + mov si,offset v_parm ;set text mode + mov bl,0 + mov dl,low(crtc) +hercloop: mov al,bl + out dx,al + inc dx + cs: lodsb + out dx,al + dec dx + inc bx + cmp bl,16 + jb hercloop + + mov ax,vidseg ;clear screen + mov es,ax + mov ax,vid_fill ;fill value + mov cx,2048 + mov di,0 + rep stosw + + mov al,29h ;enable video + mov dl,low(crtc+4) + out dx,al + ret + ; + ; initialize video variables + ; +vid_vars: mov word [m_vcrt],crtc ;video I/O port + mov si,offset v_parm ;video parameters + mov byte [m_vpgno],0 ;page number = 0 + mov byte [m_vmsel],29h ;mode register shadow + mov byte [m_vpal],30h ;palette shadow + mov word [m_vmode],7 ;force mode 7 + mov word [m_vcol],80 ;80 columns per line + mov word [m_vrow],24 ;25-1 rows + mov word [m_vpgsz],2048 ;video page size + push ds ;clear video positions per page + pop es + mov di,offset m_vpage ;& m_vcolrow + xor ax,ax + mov cx,9 ;also set page offset + rep stosw + mov ax,0b0ch ;m_vcursor cursor end line, start line + stosw + mov al,0 + stosb ;m_vpgno + ret + ; + ; INT 10 entry + ; +int10: cmp ah,10h ;max command code + jae int10_ret + sti + cld ;forward mode + push ds + push es + pusha + xor si,si ;DS = BIOS segment + mov ds,si + mov si,vidseg +#if def VID_CGA + cmp byte [m_vcrt],0d4h ;CGA ? + jnz vid_cga1 + mov si,0b800h ;CGA segment +vid_cga1: +#endif + mov es,si + mov si,ax ;calculate jump table vector + shr si,8 + shl si,1 + jmp [cs:si+int10_tab] ;jump to individual routine + +int10_ret: iret + ; + ; video dispatch table + ; + even + +int10_tab: dw v_mode ;00 = set video mode + dw v_cursor ;01 = set cursor characteristics + dw v_setcur ;02 = set cursor position + dw v_getcur ;03 = get cursor position + dw v_getpen ;04 = get light pen position + dw v_setpage ;05 = set display page + dw v_scrlup ;06 = scroll up + dw v_scrldn ;07 = scroll down + dw v_rdattr ;08 = read character attribute + dw v_wrattr ;09 = write character attribute + dw v_wrchar ;0A = write character + dw v_exit ;0B = set palette + dw v_exit ;0C = write pixel + dw v_exit ;0D = read pixel + dw v_tty ;0E = write TTY + dw v_stat ;0F = read video status + ; + ; AH=00: set video mode + ; +v_mode: +#if def VID_CGA + cmp byte [m_vcrt],0d4h ;CGA mode ? + jz v_mode9 ;:bail +#endif + call vid_init ;initialize video + call vid_vars ;set video variables +v_mode9: jmp v_exit ;return + ; + ; video mode settings + ; +v_parm: db 61h,50h,52h,0Fh,19h,06h,19h,19h,02h,0Dh,0Bh,0Ch,0,0,0,0 + ; + ; AH=01: set cursor characteristics + ; +v_cursor: mov [m_vcursor],cx ;save cursor value + mov al,10 + jmp v_set + ; + ; AH=02: set cursor position + ; +v_setcur: mov bl,0 ;page number -> index + mov si,bx + shr si,7 + mov [si+m_vcolrow],dx ;save position + cmp [m_vpgno],bh ;same page ? + jnz v_setc2 ;:no + jmp v_tty3 ;set cursor +v_setc2: jmp v_exit + ; + ; AH=03: get cursor position + ; +v_getcur: mov bp,sp ;access stack frame + mov bl,0 ;page number -> index + shr bx,7 + mov ax,[bx+m_vcolrow] ;get cursor pos from table + mov [bp._dx],ax ;-> DX + mov ax,[m_vcursor] ;cursor shape + mov [bp._cx],ax ;-> CX + jmp v_exit + ; + ; AH=04: get light pen position + ; + ; anyone remember what a light pen looks like ? ;-) + ; +v_getpen: mov bp,sp ;access stack frame + mov byte [bp._ah],0 ;not activated -> AH + jmp v_exit + ; + ; AH = 05: set video page + ; +v_setpage: mov [m_vpgno],al ;page number + shl ax,1 + mov bx,ax ;table index + shl ax,11 + mov [m_vpage],ax ;page offset + shr ax,1 + mov cx,ax +#if def VID_CGA + mov dx,[m_vcrt] +#else + mov dx,crtc +#endif + mov al,12 ;set display offset + mov ah,ch + out dx,ax + inc ax + mov ah,cl + out dx,ax + mov dx,[bx+m_vcolrow] ;page cursor position + jmp v_tty3 ;set cursor position + ; + ; AH=06: scroll window up + ; +v_scrlup: mov bl,dh ;row difference + sub bl,ch + sub bl,al ;- scroll count + inc bl ;+1 -> number of lines to scroll + sub dl,cl ;character count + inc dl + mov dh,al ;number of lines to clear + mul byte [m_vcol] ;scroll count + add ax,ax + mov si,ax ;-> start offset + mov al,ch ;start row + mul byte [m_vcol] ;* columns + add al,cl ;add start column + adc ah,0 + add ax,ax + add ax,[m_vpage] ;+ page offset + mov di,ax ;DI = destination offset + add si,ax ;add to SI -> source offset + mov ax,[m_vcol] ;calculate line skip + sub al,dl + add ax,ax + mov bp,ax + ; + ; AX = fill character + ; BL = lines to scroll + ; DL = character count + ; DH = lines to clear + ; SI = source offset + ; DI = destination offset + ; BP = columns per line - character count + ; +v_scroll: mov al," " ;fill pattern + mov ah,bh + xor cx,cx + and dh,dh ;0 lines to clear ? + jz v_scroll2 + push es + pop ds +v_scroll1: mov cl,dl ;character count + rep movsw ;scroll a line + add si,bp ;go to next line + add di,bp + dec bl + jnz v_scroll1 ;:another line + mov bl,dh +v_scroll2: mov cl,dl ;character count + rep stosw ;clear a line + add di,bp + dec bl + jnz v_scroll2 + jmp v_exit + ; + ; AH=07: scroll window down + ; +v_scrldn: sub dl,cl ;number of characters + inc dl + mov bl,dh ;row difference + sub bl,ch + sub bl,al ;- scroll count + inc bl ;+1 -> number of lines to scroll + mov ch,al ;number of lines to clear + mul byte [m_vcol] ;scroll count + add ax,ax + neg ax + mov si,ax ;-> start offset + mov al,dh ;end row + mul byte [m_vcol] ;* columns + add al,cl ;add start column + adc ah,0 + add ax,ax + add ax,[m_vpage] ;+ page offset + mov di,ax ;DI = destination offset + add si,ax ;add to SI -> source offset + mov ah,0 ;calculate line skip + mov al,dl ;- characters per line - window width + add ax,[m_vcol] ;(move is forward) + add ax,ax + neg ax + mov bp,ax + mov dh,ch ;number of lines to clear + jmp v_scroll ;do the actual scroll + ; + ; AH=08: read character + attribute + ; +v_rdattr: call v_pos ;calculate position + mov bp,sp + mov ax,[es:di] ;read character + mov [bp._ax],ax ;-> AX + jmp v_exit + ; + ; calculate video offset + ; + ; -> DI offset + ; -> AX character, attribute + ; -> SI page number * 2 + ; +v_pos: mov dh,bl ;attribute + mov dl,al ;character + mov di,bx ;save BX &pd 980406 fixed + mov bl,0 ;page -> index + mov si,bx + shr si,7 + mov ax,[si+m_vcolrow] ;get cursor position for page + shl bx,4 ;-> page offset + add bl,al ;column offset + adc bl,0 + mov al,ah ;row offset + mul byte [m_vcol] + add bx,ax + add bx,bx + xchg di,bx ;-> page offset, restore BX + mov ax,dx ;restore character, attribute + ret + ; + ; AH=09: write character + attribute + ; +v_wrattr: call v_pos ;calculate position + rep stosw ;write characters + jmp short v_exit + ; + ; AH=0A: write character only, no attribute + ; +v_wrchar: call v_pos ;calculate position +v_wrc1: stosb + inc di + loop v_wrc1 + jmp short v_exit + ; + ; AH=0E: write character TTY + ; +v_tty: + +#if def CONSOLE + + ; serial port console + ; + ; if m_vpal bit 0 is set, we copy characters to the console. + + test byte [m_console],1 ;serial port redirect ? + jz v_ttys9 ;:no + push dx + push ax + mov dx,CONSOLE+5 +v_ttys1: in al,dx ;wait for transmit ready + out iowait,ax + test al,40h + jz v_ttys1 + mov dl,low(CONSOLE) + pop ax + out dx,al + pop dx +v_ttys9: + +#endif + call v_pos + mov dx,[si+m_vcolrow] ;get cursor position + cmp al,cr + jbe v_ctrl +v_tty1: stosb + inc dl ;inc column + cmp dl,[m_vcol] ;max ? + jz v_lf0 ;yes: line feed + ; + ; set cursor position + ; +v_tty2: mov [si+m_vcolrow],dx ;save position + cmp [m_vpgno],bh ;same page ? + jnz v_exit ;:no +v_tty3: mov cx,[m_vpage] ;page offset / 2 + shr cx,1 + mov cl,dl ;column + mov al,dh ;row * column width + mul byte [m_vcol] + add cx,ax ;get address + mov al,14 ;set cursor offset +v_set: +#if def VID_CGA + mov dx,[m_vcrt] +#else + mov dx,crtc +#endif + mov ah,ch + out dx,ax + inc ax + mov ah,cl + out dx,ax +v_exit: popa + pop es + pop ds + iret + ; + ; display control characters + ; +v_ctrl: jnz v_ctrl2 ;:not carriage return + ; + ; carriage return + ; +v_cr: mov dl,0 ;beginning of line + jmp v_tty2 ;set cursor position + +v_ctrl2: cmp al,lf + jz v_lf + cmp al,bs + jz v_bs + cmp al,bell + jz v_bell +#if def MEDIAGX + cmp al,0bh ;clear to beginning of line + jz v_cr +#endif + jmp v_tty1 + ; + ; line feed + ; +v_lf0: mov dl,0 ;beginning of line +v_lf: inc dh ;increment line +#if def VID_CGA + cmp dh,[m_vrow] ;last line ? + jbe v_tty2 ;:no scroll, set cursor +#else + cmp dh,25 ;last line ? + jb v_tty2 ;:no scroll, set cursor +#endif + dec dh ;restore + push si ;pd 980422: fix LF bug + and di,0f000h ;keep page offset +#if def VID_CGA + mov al,[m_vcol] ;columns + mov ah,0 + push ax ;save columns + mov si,di ;SI = point to next line + add si,ax + add si,ax + mov bx,ax ;save... + mul byte [m_vrow] ;* rows -> characters to scroll + mov cx,ax +#else + lea si,[di+160] + mov cx,24*80 ;scroll full screen +#endif + mov ax,es ;DS = video segment + mov ds,ax + rep movsw + mov al," " ;fill with same attribute + mov ah,[di+1] +#if def VID_CGA + pop cx ;number of columns +#else + mov cx,80 ;fill bottom line +#endif + rep stosw + xor ax,ax ;restore DS + mov ds,ax + pop si ;pd 980422: fix LF bug + jmp v_tty2 + ; + ; backspace + ; +v_bs: sub dl,1 + adc dl,0 ;limit to 0 + jmp v_tty2 ;set cursor position + ; + ; bell + ; +v_bell: call beep + jmp v_exit + ; + ; AH=0F: get video status + ; +v_stat: mov bp,sp + mov ax,[m_vmode] ;& m_vcol + mov [bp._ax],ax + mov al,[m_vpgno] ;page number + mov [bp._bh],al + jmp v_exit + ; + ; display string [SI] -> TTY + ; +v_msg: cs: lodsb ;get character + and al,al ;0 = end + jz v_msg9 + mov ah,0eh ;TTY output + mov bh,0 ;page 0 + int 10h + jmp v_msg +v_msg9: ret + ; + ; ring bell + ; +beep: push ax + push cx + mov al,0b6h ;set beep timer + out timer+3,al + mov al,33h ;beep frequency + out timer+2,al + mov al,05h + out timer+2,al + in al,port61 ;enable beep + mov ah,al + or al,3 + out port61,al + xor cx,cx ;delay +bell1: out iowait,ax + loop bell1 + mov al,ah + out port61,al + pop cx + pop ax + ret diff --git a/wrap/GXM.8 b/wrap/GXM.8 new file mode 100644 index 0000000..bcf186c --- /dev/null +++ b/wrap/GXM.8 @@ -0,0 +1,1802 @@ + ; + ; System specific code + ; + ; National SC1100 CPU + ; + ; (C)1997-2003 Pascal Dornier / PC Engines; All rights reserved. + ; + ; gx12 etc. -> page numbe in GX databook, rev. 1.1 + ; sc333 etc. -> page number in SC1100 databook, rev. 1.0 + ; + + ; pd 050417 add warm start delay + ; pd 041011 fix PMR for WRAP.2 -> enable GPIO14 + ; pd 040624 lower ROM size -> 128K to support Etherboot etc. + + ; + ; Platform specific register settings + ; +#if def DRAMOPT +SDCLKRATE equ 5 * 4 ;4x rate (66 MHz at 266 MHz core) +#else +SDCLKRATE2 equ 3 * 4 ;3x rate (88.7MHz at 266 MHz core) +SDCLKRATE equ 2 * 4 ;2.5x rate (106.4MHz at 266 MHz core) +#endif + +RFSHRATE equ 3Fh * 256 ;$3F * 64 / CPU clock = refresh interval + + ; define platform-specific settings here to avoid excessive ifdef's + +#if def WRAP + +V_CCR2 equ 018 ;CCR2 gx52 + ;DISABLE suspend pins, enable ISA + ;bus write through, enable suspend + ;on halt, unlock NW bit. + + ;8400 sc12 gx114 +V8402 equ 0B680+SDCLKRATE ;8402 setting: drive strength + + ;8404 sc12 gx114 +V8404 equ 0320 ;SDCLK0, SDCLK1 enabled, shift 2 core + ;clocks, latch one core clock after + ;rising edge of SDCLK, no FIFO + ;bypass + +V905A equ 03805 ;905A Decode control 1 sc173 + ;905B Decode control 2 sc174 + ;positive decode for COM1, RTC, + ;BIOS, IDE. + +#if def WRAP2A +V_PMR equ 000F5051 ;PMR pin multiplex register sc50 + ;GPIO40, disable FPCI monitor, + ;disable AC97 clock out, IOCS1#, + ;GPIO14, GPIO12, GPIO13, serial port, + ;SERIRQ, LPC bus, GPIO0, GPIO17, + ;GPIO20, PC_BEEP, INTC#, GPIO3, + ;GPIO2. +#else +V_PMR equ 002F5051 ;PMR pin multiplex register sc50 + ;GPIO40, disable FPCI monitor, + ;disable AC97 clock out, IOCS1#, + ;GPIO15, GPIO12, GPIO13, serial port, + ;SERIRQ, LPC bus, GPIO0, GPIO17, + ;GPIO20, PC_BEEP, INTC#, GPIO3, + ;GPIO2. +#endif + +V_MCR equ 00000000 ;MCR misc config register sc53 + ;disable FPCI_MON, no zero wait state, + ;8 bit wide IOCS#, DOCCS#, disable IRTX, + ;8 bit wide ROM, SDBE0 = 0 + +V_PPCR equ 028 ;PPCR PLL power control sc63 + ;enable external PCI clock, + ;disable PLL3 = AC97 clock, + ;enable PLL4, USB/SIO clock, + ;must set bit 5 + +#if def USB_EN +V_IOCON1 equ 00d040000 ;I/O control 1 sc250 + ;enable IR, SIO; SIO config at 002E, +#else ;disable USB SMI, enable USB +V_IOCON1 equ 00d000000 ;I/O control 1 sc250 + ;enable IR, SIO; SIO config at 002E, +#endif ;disable USB SMI, disable USB + +V_LPC00 equ 00EA ;SERIRQ_SRC serial IRQ source sc200 + ;IRQ 7,6,5,3,1 from LPC + +V_LPC10 equ 0 ;LAD_EN LPC address enable sc204 + ;LPC addressing disable + +V_LPC14 equ 0 ;LAD_D0 LPC address decode 0 sc204 + ;default addresses + +#else + +V_CCR2 equ 098 ;CCR2 gx52 + ;enable suspend pins, enable ISA + ;bus write through, enable suspend + ;on halt, unlock NW bit. + +V8402 equ 0B680+SDCLKRATE ;8402 setting: drive strength + +V8404 equ 0020 ;all clocks enabled, shift 2 core + ;clocks, latch one core clock after + ;rising edge of SDCLK, no FIFO + ;bypass + +V905A equ 0A94D ;905A Decode control 1 sc173 + ;905B Decode control 2 sc174 + ;positive decode for FDC, COM1, COM2, + ;RTC, KBC alternate, BIOS, IDE, LPT1. + +V_PMR equ 02095155 ;PMR pin multiplex register sc50 + ;GPIO40, disable FPCI monitor, + ;enable AC97 clock out, IOCS1#, + ;GPIO15, GPIO12, GPIO13, second IDE, + ;SERIRQ, LPC bus, GPIO0, GPIO17, + ;GPIO20, PC_BEEP, INTC#, GPIO3, + ;GPIO2. + +V_MCR equ 00000010 ;MCR misc config register sc53 + ;disable FPCI_MON, no zero wait state, + ;8 bit wide IOCS#, DOCCS#, enable IRTX, + ;8 bit wide ROM, SDBE0 = 0 + +V_PPCR equ 020 ;PPCR PLL power control sc63 + ;enable external PCI clock, PLL3, + ;PLL4, USB/SIO clock, set reserved bit + +V_IOCON1 equ 00d040000 ;I/O control 1 sc250 + ;enable IR, SIO, USB; config at 002E, + ;disable USB SMI + +V_LPC00 equ 011DA ;SERIRQ_SRC serial IRQ source sc200 + ;IRQ12,8,7,6,4,3,1 from LPC + +V_LPC10 equ 01F9B8 ;LAD_EN LPC address enable sc204 + ;LPC RTC, KBC, wide generic, FDC, + ;COM, LPT + +V_LPC14 equ 00020 ;LAD_D0 LPC address decode 0 sc204 + ;FDC @ 3F0, COM1 03F8, COM2 02F8 + +#endif + + ; + ; Equates + ; +cx_idx equ 22h ;Cyrix index register +cx_dat equ 23h ;Cyrix data register +gx_base equ 4000h ;GX register base (don't change, + ;this value is used in many places) +GX_GDT: ;enable special GDT entry (post.8) + + ;SDRAM clock rate + ;(1) 2x (2) 2.5x (3) 3x (4) 3.5x + ;(5) 4x (6) 4.5x (7) 5x +SMIBASE equ 0F000 ;SMI base +XBUSBASE equ 0F200 ;XBUS base +GPIOBASE equ 0F400 ;GPIO base +LPCBASE equ 0F600 ;LPC base +CFGBASE equ 09000 ;Geode config base + ;warning: aliased 8000..BFFF + ;(see errata) +ACPIBASE equ 0FA00 ;ACPI base +IDEBASE equ 0FC00 ;IDE base + + ; all of these must be in the same 64K block + +HIGHBASE equ 04001 ;high memory base +SMIBASE2 equ 02000 ;SMI memory base L +AUDBASE equ 01000 ;audio memory base L +VIDBASE equ 00000 ;video / clock memory base L + + ; USB is separate + +HCIBASE_H equ 0F000 ;HCI USB base +HCIBASE_L equ 00000 + + ; PCI defines + +TOP_MEM equ 02000 ;memory size limit equ 512 MB + +PCI: ;Undefine if not PCI based + + ;memory: 64K multiples +P_MEM0 equ 08000 ;start address, PCI memory space +P_MEM9 equ 09000 ;end address, PCI memory space +P_ROM0 equ 09000 ;temporary address for PCI ROM +P_MEMP0 equ 0A000 ;start address, PCI prefetchable space +P_MEMP9 equ 0B000 ;end address, PCI prefetchable space +P_MEMINC equ 00004 ;minimum allocation +P_MEMINC1 equ 00100 ;mem allocation, primary bridge +P_MEMINC2 equ 00040 ;mem allocation, secondary bridge + + ;real mode: 16 byte multiples +P_MEMR0 equ 0c800 ;real mode memory start +P_MEMR9 equ 0e000 ;real mode memory limit +P_MEMRINC equ 00400 ;real mode minimum allocation + + ;I/O: +P_IO0 equ 01000 ;start address, PCI I/O +P_IOINC equ 00040 ;minimum allocation for I/O devices +P_IOINC1 equ 04000 ;I/O allocation, primary bridge +P_IOINC2 equ 01000 ;I/O allocation, secondary bridge +P_IO9 equ 07FFF ;I/O limit = below chipset blocks + +P_LINSIZE equ 008 ;line size = 32 bytes +P_PRILAT equ 000 ;primary latency timer +P_SECLAT equ 000 ;bridge secondary latency timer + +P_BRIDGE equ 00000111xb ;PCI bridge control register + ;bit 0 = 1: enable parity check + ;bit 1 = 1: forward serr# to primary + ;bit 2 = 1: ISA mode for I/O registers + ;bit 3 = 1: VGA enable + ;bit 5 = 1: master abort mode + ;bit 6 = 1: secondary bus reset + ;bit 7 = 1: fast back to back enable + ; (set automatically) + +P_COMMAND = 0100001111xb ;PCI device command register + ;bit 0 = 1: enable I/O access + ;bit 1 = 1: enable memory access + ;bit 2 = 1: enable bus master access + ;bit 3 = 1: enable special cycles + ;bit 4 = 1: enable mem write / inval + ;bit 5 = 1: enable VGA palette snoop + ;bit 6 = 1: enable parity checking + ;bit 7 = 1: enable AD stepping + ;bit 8 = 1: enable SERR# driver + ;bit 9 = 1: enable fast back to back + ; + ; First chipset init + ; +cs_clr: + ; initialize Geode configuration block + + mov dx,CFGBASE+03e ;CBA configuration base address sc54 + in ax,dx ;already set ? + cmp ax,CFGBASE + jz cs_clr0 ;:yes + mov ax,CFGBASE ;set base address + mov dx,002ea ;one time use port + out dx,ax + +cs_clr0: mov dx,CFGBASE+030 ;PMR pin multiplex sc50 + in eax,dx + and eax,00002c00 ;"write as read" + or eax,V_PMR ;see at start + out dx,eax + + mov dl,034h ;MCR misc config sc53 + in eax,dx + and eax,00000002 ;"write as read" + or eax,V_MCR + out dx,eax + + mov dl,012 ;PPCR PLL power control sc63 + in al,dx + and al,091 ;"write as read" + or al,V_PPCR + out dx,al + + mov dl,2 ;WDTO, WDCNFG watchdog sc56 + in ax,dx + and ax,0fe00 ;"write as read" +; or ax,00000 ;disable + out dx,ax + + mov dl,0c ;TMSTS, TMCNFG timer sc58 + mov ax,0001 ;clear timer status, enable 27 MHz, + out dx,ax ;microsecond timer, HRT int disable + + ; other registers: + ; + ; 04 ;WDSTS watchdog status sc57 + ; 08 ;TMVALUE hires timer sc58 RO + ; 10 ;MCCM max core mult. sc63 RO + ; 18 ??? 7601d004 ;PLL3C PLL3 config sc63 + ; 1E 0204 fast PCI, 266 MHz ;CCFC core clock control sc64 + ; 38 ;INTSEL interrupt select sc54 + + ; delay a bit if this is a warm start pd 050417 + ; + ; the intention of this is to give the CF card time to write back + ; data before we do a IDE and PCI reset + + mov dx,CFGBASE+4 ;watchdog status sc57 + in al,dx + test al,8 ;asserted ? + jnz cs_clrw ;-> delay + + mov eax,08000905ch ;905C PCI interrupt map + mov dx,pci_ad + out dx,eax + mov dl,low(pci_dat) + in ax,dx + and ax,ax ;if not zero, it was a BIOS reboot + jnz cs_clrw ;-> delay + + mov ax,09044h ;9044 reset control sc170 + mov dl,low(pci_ad) + out dx,eax + mov dl,low(pci_dat) + in al,dx + test al,01 + jnz cs_clrw2 ;bit 0 set = cold start -> skip delay + +cs_clrw: mov cx,50000 ;warm start -> wait a bit... +cs_clrw1: out iowait,ax + dec cx + jnz cs_clrw1 +cs_clrw2: + + ; init PCI mapped chipset registers + + mov eax,80000000h ;PCI configuration space + mov si,offset gxtab0 +cs_clr1: cmp si,offset gxtab0w + jnz cs_clr1b + + ; add some reset delay - PCI reset must be 1 ms+ + + mov cx,200 +cs_clr1a: out iowait,ax + out iowait,ax + out iowait,ax + out iowait,ax + loop cs_clr1a + +cs_clr1b: cs: lodsw ;register index + xchg eax,ebx + cs: lodsd ;data + xchg eax,ebx + mov dx,pci_ad + out dx,eax ;write index + mov dl,low(pci_dat) + xchg eax,ebx + out dx,eax ;write data + xchg eax,ebx + cmp si,offset gxtab0e ;end of table ? + jb cs_clr1 + + ; enable SIO + + mov dx,XBUSBASE+0 ;I/O control 1 sc250 + in eax,dx + and eax,0f0001ffff ;reserved bits + or eax,V_IOCON1 + + ;USB enable if configured + cmp byte [cs:CFG_OFS+cfg_usb],0 + jz cs_clr2 ;USB disable + or eax,040000 ;enable USB +cs_clr2: out dx,eax ;(if USB_EN is set, USB is always + ;enabled) + + ; initialize I/O mapped registers + + mov dx,LPCBASE+010 ;LAD_EN LPC address enable sc204 + in eax,dx + and eax,08000 ;keep LPC flash enable bit ! + or eax,V_LPC10 + out dx,eax + + mov si,offset gxtab10 +cs_clr3: cs: lodsw ;get index word + mov dx,ax + cs: lodsd ;get data word + out dx,eax ;write to port + cmp si,offset gxtab10e ;end of table ? + jb cs_clr3 ;:no + + jmp sio_init0 ;do early super I/O initialization + ; + ; initialize chipset + ; + ; set all registers, detect memory size + ; keep L2 cache disabled at this point + ; set CPU specific registers if required + ; +cs_init: mov ax,cs ;DS = CS + mov ds,ax + ; + ; Enter unreal (4GB segment) mode -> change ES, GS selector + ; + ; This selector is not touched by other code, and should stay + ; valid until we get ready to boot. + ; + cli ;disable interrupts + lgdt word [gdt] ;load GDT (in data module, writeable) + + mov eax,cr0 + or al,1 ;enable protected mode + mov cr0,eax + jmp short loadgs ;flush queue +loadgs: mov bx,8 ;selector for 4 GB + mov es,bx + mov bx,10h ;selector for GX_BASE + mov gs,bx ;mov gs,bx + and al,0fe ;exit protected mode + mov cr0,eax ;mov cr0,eax + ; + ; load CPU configuration registers (port 22 / 23) + ; + mov si,offset gxtab1 ;CPU configuration registers +cs_ini01: lodsw ;get index / data from table + out cx_idx,al + mov al,ah + out cx_dat,al + cmp si,offset gxtab1e ;end of table ? + jb cs_ini01 + ; + ; disable CPU cache + ; + mov eax,cr0 + or eax,040000000h ;set CD bit = disable cache + and eax,0dfffffffh ;clear NW bit -> write through + mov cr0,eax + invd + ; + ; is shadow on ? if yes, don't overwrite shadow / DRAM config + ; registers + ; + mov si,offset gxtab2 ;initialize from scratch + mov di,[gs:800eh] ;BC_XMAP_3 gx105 + and di,di ;shadow on ? + jz cs_ini03 ;:no + mov si,offset gxtab3 ;skip shadow / DRAM registers + ; + ; initialize GX registers, accessed through GS: segment + ; (points to GX_BASE). + ; +cs_ini03: mov bx,[si] ;get index + mov eax,[si+2] ;get data + add si,6 + + ; option for conservative DRAM timing + +#if def cfg_dram + cmp bx,08400 ;DRAM timing config ? + jnz cs_ini03b + test byte [cs:CFG_OFS+cfg_dram],0ff + jz cs_ini03b + and eax,0ffe3ffff ;clear bits 20:18 + or eax,SDCLKRATE2 * 65536 +cs_ini03b: +#endif + mov [gs:bx],eax ;write register + cmp si,offset gxtab3e ;end of table ? + jb cs_ini03 + ; + ; initialize GX registers, accessed through CPU_WRITE instruction + ; no longer supported on SC1100 ? + ; +;cs_ini04: mov si,offset gxtab4 +;cs_ini05: mov ebx,0ffffffffh +; mov bx,[si] ;index +; mov eax,[si+2] ;get data +; add si,6 +; db 00f,03c ;cpu_write ;write register +; cmp si,offset gxtab4e +; jb cs_ini05 + ; + ; initialize PCI config registers + ; + mov eax,080000000h ;PCI configuration space + mov si,offset gxtab6 +cs_ini09: lodsw ;register index + xchg eax,ebx + lodsd ;data + xchg eax,ebx + mov dx,pci_ad + out dx,eax ;write index + mov dl,low(pci_dat) + xchg eax,ebx + out dx,eax ;write data + xchg eax,ebx + cmp si,offset gxtab6e ;end of table ? + jb cs_ini09 + ; + ; initialize 5540 memory mapped registers + ; + mov edi,HIGHBASE shl 16 ;point to HIGHBASE + mov si,offset gxtab9 +cs_inic1: cs: lodsw ;get index + mov di,ax + lodsd ;get data + a4 stosd ;write register + cmp si,offset gxtab9e ;end of table ? + jb cs_inic1 + ; + ; initialize GPIO pin map + ; + mov si,offset gxtab11 + xor eax,eax + xor bx,bx ;clear index +cs_ini16: mov al,bl ;get index + mov dx,GPIOBASE+20h ;GPIO pin config select sc198 + out dx,eax + lodsb + mov dl,24h ;GPIO pin config access sc198 + out dx,eax + inc bx ;next index + cmp bx,64 ;end ? + jb cs_ini16 + +#if def WRAP + + ; to allow access to ACPI registers, we need to give PWRBTN# + ; a kick + + mov dx,GPIOBASE+10h ;GPIO data out 1 sc197 + in eax,dx + and eax,0ffff7fff ;clear G47 = PWRBTN# + out dx,eax + + ; waste 20+ ms for PWRBTN# debounce + + mov cx,2000 +cs_iniwr: out iowait,ax + out iowait,ax + out iowait,ax + out iowait,ax + out iowait,ax + out iowait,ax + out iowait,ax + out iowait,ax + loop cs_iniwr + + ; now release PWRBTN# + + or ax,08000 ;set G47 = PWRBTN# + out dx,eax +#endif + ; + ; initialize 8/16/32 bit GPIO registers + ; + mov si,offset gxtab12 +cs_ini17: cmp si,offset gxtab12e ;end ? + jae cs_ini18 + mov cl,[si+0] ;length code + mov dx,[si+1] ;register address + mov eax,[si+3] ;data + dec cl ;length ? + js cs_ini17b ;0 :byte + jnz cs_ini17l ;2 :long word + out dx,ax ;1 :word + add si,5 + jmp cs_ini17 + +cs_ini17b: out dx,al ;byte + add si,5 + jmp cs_ini17 + +cs_ini17l: out dx,eax ;long word + add si,7 + jmp cs_ini17 +cs_ini18: + ; + ; return carry set if we had shadow on before + ; +cs_ini90: mov di,[gs:800eh] ;BC_XMAP_3 gx105 + cmp di,0 + jz cs_ini91 + stc ;shadow was on, set carry + ret + +cs_ini91: clc ;shadow was off - clear carry + ret + ; + ; CPU specific settings + ; +cs_cpu: + +#if def GX_PAD + + ; Initialize scratch pad - need to set the tag + + ; Scratch pad doesn't make that much sense on SC1100 unless + ; you have some small, super time critical real-time task... + ; This has been tested on earlier CPUs, but not on SC1100. + + mov eax,GX_BASE shl 16 ;point L1 tag to start of GX space + db 00f,026,11100000xb ;mov tr4,eax + + mov eax,0401h ;start address + 1 +cs_pad: db 00f,026,11101000xb ;mov tr5,eax + add ax,0010h ;next cache line + cmp ax,1000h + jb cs_pad ;:another line + + ; now enable the scratch pad, this locks down the tag + + mov al,0B8h ;GCR Graphics control register gx97 + out cx_idx,al + mov al,5 ;enable scratch pad = 2KB + out cx_dat,al ;(SC1100 doesn't support 3KB or 4KB !) +#endif + + invd ;flush cache + mov eax,cr0 + and eax,09fffffffh ;clear CD -> enable cache + or eax,20000000h ;set NW bit -> write back enable + mov cr0,eax + jmp short cs_cpu1 + +cs_cpu1: invd + + mov al,0c2h ;set LOCK_NW to lock state of NW bit + out cx_idx,al + xchg ax,bx + in al,cx_dat + xchg ax,bx + out cx_idx,al + xchg ax,bx + or al,4 + out cx_idx,al + +#if def DRAMOPT + jmp sdramopt ;optimize SDRAM timing +#else + ret +#endif + ; + ; Detect memory + ; + ; EAX = test pattern (most of the time) + ; EBX = inverted test pattern + ; CL = bank (16,0), used as shift for values + ; EDX = DRAM configuration all banks (shifted left) + ; EBP = total memory size + ; SP = return pointer + ; DS = 0 (real mode) + ; ES = 0 (unreal mode) + ; + ; Cache must be off ! 8KB, 16KB page sizes are not shown in Cyrix' + ; address map (m129), and not supported. + ; +cs_det: mov cl,16 ;start with DIMM1 + xor ax,ax ;clear DS + mov ds,ax + xor ebp,ebp ;clear total memory size + + ; set bank for maximum size, bank count etc. + +cs_det1: shl edx,16 ;push up previous configuration + mov eax,00705720h ;set 2 DIMM banks, 4 component banks, + ;4KB page size, 512MB DIMM size + rol eax,cl ;swap DIMMs if necessary + mov [gs:8408h],eax ;set memory type gx114 + in ax,iowait ;force delay + + ; test address 0 -> bank presence + + xor edi,edi ;test words at 0 + mov eax,033cc55aa ;test pattern + stosd + not eax ;invert bus + stosd + stosd + mov ebx,eax ;inverted pattern for later + not eax ;did this location work ? + cmp eax,[0] + jz cs_det1a +cs_det1x: mov dx,0070h ;no memory + jmp cs_det8 +cs_det1a: + mov dl,0 ;ok, set 1KB page size + + ; test A13 - 2 or 4 component banks ? + + mov [2000h],ebx + cmp eax,[0] ;overwrite ? + jnz cs_det2 ;:yes + or dh,10h ;4 component banks +cs_det2: mov [0],eax ;restore reference pattern + + ; test A14 - 1 or 2 module banks ? + + mov [4000h],ebx ;write pattern + mov [4008h],eax + cmp [4000h],ebx + jnz cs_det3 ;:bad + or dh,40h ;2 component banks +cs_det3: + ; test A10 - 1KB page size ? + + mov [0400h],ebx + cmp eax,[0] ;overwrite ? + jnz cs_det4 ;:yes + add dl,10h ;increase page size + + ; test A11 - 2KB page size + + mov [0800h],ebx + cmp eax,[0] ;overwrite ? + jnz cs_det4 ;:yes + add dl,10h ;increase page size +cs_det4: + ; set bank parameters + + mov eax,00700700h ;other bank disabled + or ax,dx ;get bank parameters, or with 512MB size + rol eax,cl ;swap DIMMs if necessary + mov [gs:8408h],eax ;set memory type gx114 + in ax,iowait ;force delay + + ; detect DIMM size + + mov ch,7 ;7 iterations + mov edi,00400000h ;start with 4MB + mov eax,ebx ;restore test pattern + not eax + mov [0],eax +cs_det5: mov [es:edi],ebx ;write at [size] + cmp eax,[0] ;did [0] get clobbered ? + jnz cs_det6 ;yes: we reached DRAM size + inc dh ;increase RAM size count + shl edi,1 ;next RAM size + dec ch + jnz cs_det5 +cs_det6: add ebp,edi ;sum up RAM size + + ; another bank ? + +cs_det8: xor cl,16 + jnz cs_det9 + jmp cs_det1 ;:yes + + ; set final value + +cs_det9: mov [gs:8408h],edx ;set memory type gx114 + dec ebp ;low bits = 1 + mov [gs:8000h],ebp ;BC_DRAM_TOP gx104 + ret + ; + ; Copy BIOS to shadow RAM (skip if already enabled) + ; + ; Note: For fastest startup, this happens before DRAM test and + ; BIOS checksum test. If DRAM or BIOS is bad, we might crash. + ; +cs_shad: cmp word [gs:800eh],0 ;is shadow on ? + jnz cs_shad9 ;yes: don't touch + mov dword [gs:800ch],22222222h ;write only mode gx105 + ; + ; flush cache the hard way (through test registers) + ; + xor cx,cx ;line, set number +cs_shad01: mov eax,0f0000000h ;clear tag + mov ax,cx ;bits 15..14 = line + shl ax,14 + db 00f,026,11100000xb ;mov tr4,eax + mov ax,cx ;line, set + shl ax,2 + or al,1 ;cache write + db 00f,026,11101000xb ;mov tr5,eax + inc cx + cmp cx,0400 + jnz cs_shad01 + ; + ; copy BIOS to shadow RAM + ; + mov ax,0e000h ;E000 segment + mov ds,ax + mov es,ax + xor si,si + xor di,di + mov cx,4000h ;copy + rep movsd + mov ax,cs ;F000 segment + mov ds,ax + mov es,ax + mov cx,4000h + rep movsd ;rep movsd + wbinvd ;flush cache (just in case) + in ax,iowait ;stall to make sure all is written back +#if def SMI + mov dword [gs:800ch],0ddd7ddddh ;enable read only / cache gx105 + ;SMI: F000-F3FF read/write +#else + mov dword [gs:800ch],0ddddddddh ;enable read only / cache gx105 +#endif +cs_shad9: ret + ; + ; Copy ISA video BIOS to shadow RAM + ; +cs_vshad: push ds + push es + + mov ax,0c000h ;video BIOS segment + mov es,ax +#if def VBIOSSRC ;if we have video BIOS in "weird" place + mov ax,VBIOSSRC +#endif + mov ds,ax + cmp word [0],0aa55h ;video BIOS signature ? + jnz cs_vshad1 ;no: no video BIOS on ISA bus, bail + + mov ebx,[gs:8008h] ;get shadow register + or bl,bl ;video shadow on ? + jnz cs_vshad1 ;yes: don't touch + mov bl,22h ;set write only mode for C000-C7FF + mov [gs:8008h],ebx + + xor di,di ;copy BIOS to shadow RAM + xor si,si + mov cx,8000h/4 + rep movsd + + wbinvd + in ax,iowait ;stall to make sure all is written back + + mov bl,0ddh ;set read only mode + mov [gs:8008h],ebx + +cs_vshad1: pop es + pop ds + ret + ; + ; Copy video BIOS from PCI ROM to shadow RAM + ; DS:ESI = unreal mode pointer to BIOS + ; ES = destroyed + ; +cs_vshad2: push es + pushad + + mov ebx,[gs:8008h] ;get shadow register + or bl,bl ;video shadow on ? + jnz cs_vshad9 ;yes: don't touch + mov bl,22h ;set write only mode for C000-C7FF + mov [gs:8008h],ebx + + mov ax,0c000h ;copy video BIOS from PCI ROM + mov es,ax ;to shadow + xor edi,edi + mov ecx,8000h/4 + a4 rep movsd + + wbinvd ;flush cache (just in case) + in ax,iowait ;stall to make sure all is written back + + mov bl,0ddh ;set read only mode + mov [gs:8008h],ebx + popad + pop es +cs_vshad9: ret + ; + ; set speed-sensitive chipset registers + ; +cs_spd: ret + ; + ; Set read/write shadow + ; + ; The BIOS is written to for HDD parameters. + ; +cs_shadrw: mov dword [gs:800ch],0ffffffffh ;set shadow status gx105 -> read, + ret ;write, cacheable + ; + ; set read only shadow + ; +cs_shadro: +#if def SMI + mov dword [gs:800ch],0ddd7ddddh ;set shadow status gx105 -> read, + ;cacheable. SMI: F000-F3FF read/write +#else + mov dword [gs:800ch],0ddddddddh ;set shadow status gx105 -> read, + ;cacheable. +#endif + ret + ; + ; enable A20 gate (note this is really controlled by register inside + ; GXM) + ; +cs_a20on: mov al,26h ;enable A20 gate + out port92,al + ret + ; + ; Wait BX milliseconds - depends on refresh rate !!! + ; + ; This is used for floppy delays and INT15 function 86. + ; +cs_waitbx: inc bx + jmp short cs_wbx8 + +cs_wbx1: mov cx,62 ;62 refresh cycles per millisecond +cs_wbx2: in al,port61 + and al,10h + mov ah,al +cs_wbx3: in al,port61 ;wait for refresh bit to change state + and al,10h + cmp al,ah + jz cs_wbx3 + loop cs_wbx2 ;:another iteration +cs_wbx8: dec bx ;another millisecond ? + jnz cs_wbx1 +cs_wbx9: ret + ; + ; Test and enable numeric coprocessor + ; +cs_npx: fninit ;initialize x87 + fstcw [tmp_npx] ;store status + cmp word [tmp_npx],037fh ;present + jnz cs_npx7 ;:no + + in al,pic1+1 ;enable interrupt + and al,11011111xb + out pic1+1,al + or byte [m_devflg],2 ;set device flag + + mov eax,cr0 + and al,11011011xb ;clear EM, NE bits + or al,00000010xb ;set MP bit + mov cr0,eax + ret + +cs_npx7: mov eax,cr0 + or al,00100100xb ;set EM, NE bits + and al,11111101xb ;clear MP bit + mov cr0,eax + ret + ; + ; configure PCI interrupts [eax] + ; 0..7 = INTA, 8..15 = INTB, 16..23 = INTC, 24..31 = INTD + ; +cs_pciint: mov ch,4 ;4 interrupts + xor bx,bx ;interrupt map + xor dx,dx ;interrupt bitmap +cs_pcii1: rol eax,8 ;start with INTD + cmp al,0ffh ;unavailable + jnz cs_pcii2 + mov al,0 ;0 = disable +cs_pcii2: shl bx,4 + or bl,al ;include in BX + mov si,1 ;set corresponding bit in DX + mov cl,al + shl si,cl + or dx,si + dec ch ;another interrupt ? + jnz cs_pcii1 ;:yes + + xchg ax,dx ;DX -> AX (interrupt bitmap) + and al,0feh ;clear bit 0 + mov dx,picedge0 + out dx,ax ;set edge/level mode (& picedge1) + + xchg ax,bx ;BX -> AX + mov ebx,08000905c ;5C / 5D PCI interrupt map sc174 + jmp pci_setw + + ; + ; 5530 registers, access through PCI configuration space + ; +gxtab0: dw 09014,LPCBASE+1 ;9014 F0BAR1 LPC base sc168 + dw 00000 + dw 09010,GPIOBASE+1 ;9010 F0BAR0 GPIO base sc168 + dw 00000 + + dw 09044,0008e ;&&& ;9044 Reset control sc170 + dw 006EE ;9046 PCI functions enable sc171 + ;-> reset AC97, IDE +gxtab0w: dw 09044,00000 ;9044 Reset control sc170 + dw 006EE ;9046 PCI functions enable sc171 + ;9047 Misc enable sc171 + ;enable LPC, GPIO + + dw 09058,00000 + dw V905A ;905A Decode control 1 sc173 + ;905B Decode control 2 sc174 + +; dw 09000,0100B ;9000 Vendor ID sc166 RO +; dw 00510 ;9002 Device ID sc166 RO + dw 09004,0000F ;9004 PCI command sc166 + dw 00280 ;9006 PCI status sc167 +; dw 09008,00000 ;9008 Revision sc167 RO +; ;9009 Class code sc167 RO +; dw 00601 + + dw 09540,0FFC1 ;9540 F5BAR0 mask sc247 + dw 0FFFF ;64 bytes + dw 09544,0FFC0 ;9544 F5BAR1 mask sc248 + dw 0FFFF ;64 bytes + dw 09548,0FFC0 ;9548 F5BAR2 mask sc248 + dw 0FFFF ;64 bytes + dw 0954C,0FFC0 ;954C F5BAR3 mask sc248 + dw 0FFFF ;64 bytes + dw 09550,0FFC0 ;9550 F5BAR4 mask sc248 + dw 0FFFF ;64 bytes + dw 09554,0FFC0 ;9554 F5BAR5 mask sc248 + dw 0FFFF ;64 bytes +; dw 09558,00000,0 ;9558 F4BAR/F5BAR target sc249 +; dw 09560,0,0 ;9560 BIOS scratch sc249 + dw 09564,CFGBASE,0 ;9564 BIOS scratch sc249 + + dw 09510,XBUSBASE+1,0 ;9510 F5BAR0 X-Bus 0 sc246 +; dw 09514,0,0 ;9514 F5BAR1 reserved sc246 +; dw 09518,0,0 ;9518 F5BAR2 reserved sc246 +; dw 0951C,0,0 ;951C F5BAR3 reserved sc246 +; dw 09520,0,0 ;9520 F5BAR4CS sc247 +; dw 09524,0,0 ;9520 F5BAR5CS sc247 + +; dw 09500,0100B ;9500 Vendor ID sc246 RO +; dw 00515 ;9502 Device ID sc246 RO + dw 09504,00003 ;9504 PCI command sc246 + dw 00280 ;9506 PCI status sc246 RO +; dw 09508,00000 ;9508 Revision sc246 RO +; ;9509 Class code sc246 RO +; dw 00680 +; dw 0950C,00000 ;950C Cache line size sc246 RO +; ;950D PCI latency timer sc246 RO +; w 00000 ;950E PCI header type sc246 RO +; ;950F PCI BIST sc246 RO + + dw 0900C,04000;0008 ;900C Cache line size sc168 + ;900D PCI latency timer sc168 + dw 00080 ;900E Header type sc168 RO + ;900F PCI BIST sc192 RO + +; dw 0902C,0100B ;902C Subsystem vendor ID sc168 RO +; dw 00500 ;902E Subsystem device ID sc168 RO + + dw 09040,00019 ;9040 PCI function control 1 sc169 + ;disable PERR/SERR, + ;enable PCI interrupt acknowledge + ;9041 PCI function control 2 sc169 + ;disable config traps + dw 00000 ;9042 reserved sc169 + ;9043 PIT delayed transactions sc170 + ;immediate transactions + ;set 0200 for benchmark / diagnostic + ;compatibility, 0000 for performance. + + dw 09110,SMIBASE+1 ;9110 F1BAR0 SMI base sc207 + dw 00000 + dw 09140,ACPIBASE+1 ;9140 F1BAR1 ACPI base sc207 + dw 00000 + + dw 09220,IDEBASE+1 ;9220 F2BAR4 IDE base sc225 + dw 00000 + + dw 09310,AUDBASE ;9310 F3BAR0 audio base sc231 + dw HIGHBASE + + dw 09810,HCIBASE_L ;9810 HCI base address sc252 + dw HCIBASE_H +gxtab0e: ;end of table + ; + ; GX CPU configuration registers, accessed through cx_idx / cx_dat + ; +gxtab1: db 0C1,000 ;CCR1 gx52 + ;disable SMI + db 0C3,014 ;CCR3 gx52 + ;this must be set first to allow + ;access to other config registers. + ;enable config registers; serialize + ;PCI memory space. + ;04 reserved bit, set per GXMREG.ASM + db 020,020 ;PCR Performance control gx53 + ;disable VGA SMI + db 0B8,001 ;GCR Graphics control register gx97 + ;no scratch pad, GX_BASE 04000 + db 0C2,V_CCR2 ;CCR2 gx52 + db 0E8,0B8 ;0F8 ;CCR4 gx53 + ;enable CPUID, DISable nested SMI, + ;enable directory table entry cache, + ;enable memory read bypassing, + ;no I/O recovery time, fast FPU mode + db 0C3,0F8 ;CCR3 gx52 + ;enable config registers; serialize + ;PCI memory space. SMM not locked. + + ;NOTE: this will clobber SMM header + ;address (below) - side effect of + ;write + db 0CD,000 ;040 ;SMM address MSB 0 gx54 + db 0CE,00f ;040 ;SMM address 1 gx54 +#if def SMI + db 0CF,003 ;size: 16KB +#else + db 0CF,000 ;006 ;SMM address LSB, size 2 gx54 + ;disable SMI (normal: 06) +#endif + + db 0B0,000 ;SMM header address LSB 0 gx53 + db 0B1,040 ;000 ;SMM header address 1 gx53 + db 0B2,00f ;041 ;SMM header address 2 gx53 + db 0B3,000 ;040 ;SMM header address MSB 3 gx53 + + db 0B9,000 ;VGA control register gx164 + ;SMI disabled + db 0BA,000 ;VGA mask register 0 gx164 + db 0BB,000 ;VGA mask register 1 gx164 + db 0BC,000 ;VGA mask register 2 gx164 + db 0BD,000 ;VGA mask register 3 gx164 + ;SMI disabled + + db 0EB,001 ;CCR7 Configuration control 7 gx53 + ;no NMI, enable extended MMX + db 0F0,003 ;PCR1 performance control 1 gx55 + ;enable incrementor, 1X clock +; db 0FE,0B0 ;Device ID 0 gx54 + ;SC1200 device ID, core multiplier +; db 0FF,081 ;Device ID 1 gx54 + ;revision number +gxtab1e: ;end of table + ; + ; GX CPU configuration registers, accessed through GX_BASE + index + ; +gxtab2: dw 08300,04758,00000 ;DC unlock m157 -> unlocked gx144 + dw 08304,00000,00000 ;DC general config gx144 + + dw 08000,0FFFF,01FFF ;BC_DRAM_TOP 512MB - 1 gx104 + dw 08004,00060,00000 ;BC_XMAP_1 memory xbus map gx104 + ;no VGA / shadow, A20 enable + dw 08008,00000,00000 ;BC_XMAP_2 C0..DF map gx105 + dw 0800C,00000,00000 ;BC_XMAP_3 E0..FF map gx105 + + dw 08404,V8404,00000 ;MC_MEM_CNTRL2 mem config 2 gx114 sc12 + ;(set before 8400 for proper SDCLK + ;init protocol) + dw 08400,RFSHRATE+00C ;MC config 1 gx113 + dw V8402 ;disable SDRAM clock + dw 08400,RFSHRATE+00C ;MC config 1 gx113 + dw V8402+2 ;start SDRAM clock + +#if def DRAMOPT ;DRAM optimization: start at PC66 timing + dw 0840C,03125,03753 +#else ;else, use aggressive PC133 timing + dw 0840C,03125,02643 ;MC_SYNC_TIM1 DRAM timing gx115 +#endif +; dw 0840C,03115,03753 ;(earlier value) +; dw 0840C,02110,03542 ;(GXM value) + ;CAS latency 3 clk, + ;REF->REF/ACT tRC 7 clk, + ;ACT->PRE tRAS 5 clk, + ;PRE->ACT tRP 3 clk, + ;ACT->R/W tRCD 3 clk, + ;ACT0<->ACT1 tRRD 1 clk, + ;Data in -> PRE tDPL 2 clk + dw 08400,RFSHRATE+00D ;MC_MEM_CNTRL1 gx113 - pulse MRS + dw V8402+2 + dw 08400,RFSHRATE+00C ;MC_MEM_CNTRL1 mem config 1 gx113 + dw V8402+2 ;SDRAM clock / 3.5 (76 MHz), + ;enable SDRAM clock, refresh rate 3F, + ;refresh not staggered, enable SMM map + dw 08408,01310,00070 ;MC_BANK_CFG bank address gx114 + ;DIMM1 1 module bank, 2 component banks, + ;4MB size, not installed + ;DIMM0 1 module bank, 4 component banks, + ;32MB size, 2KB bank size + +; dw 08418,003FF,00000 ;MC_DR_ADD Dirty RAM address gx116 +; dw 0841C,003FC,00000 ;MC_DR_ACC Dirty RAM access gx116 + ; + ; these registers can be reprogrammed without blowing up shadow + ; BIOS + ; +gxtab3: dw 08414,007ff,00000 ;Graphics memory base gx116 + +; dw 08100,00280,00050 ;X,Y coordinate gx130 +; dw 08104,00008,00010 ;Width, height / vector length, error gx130 +; dw 08108,00000,00000 ;BLT X, Y source / axial, diag error gx130 +; dw 0810C,00000,00707 ;GP source color 0,1 gx131 +; dw 08110,0FFFF,0FFFF ;GP pattern color 0,1 gx131 +; dw 08114,0FFFF,0FFFF ;GP pattern color 2,3 gx131 +; dw 08120,0FFFF,0FFFF ;GP pattern data 0 gx131 +; dw 08124,0FFFF,0FFFF ;GP pattern data 1 gx131 +; dw 08128,0FFFF,0FFFF ;GP pattern data 2 gx131 +; dw 0812C,0FFFF,0FFFF ;GP pattern data 3 gx131 +; dw 08140,000FF,00300 ;VGA write 4-15 gx166 +; dw 08144,09E00,00000 ;VGA read 4-16 gx166 + +; dw 08200,000CC,000CC ;GP raster mode gx132 +; dw 08204,00280,00280 ;GP vector mode gx132 +; dw 08208,002C3,002C3 ;GP BLT mode gx132 +; dw 0820C,00000,00000 ;GP BLT status gx133 + ;screen width 1024, 8bpp +; dw 08210,00000,00000 ;VGA base address gx166 +; dw 08214,00000,00000 ;VGA latch 4-14 gx166 + + +;; dw 08300,04758,00000 ;DC unlock m157 -> unlocked gx144 +;; dw 08304,07640,00000 ;DC general config gx144 +;; dw 08304,06540,00000 ;DC general config gx144 + ;FIFO end level 7, start 6, + ;DCLK 2x, compression disable, + ;cursor disable, FIFO disabled +;; dw 08308,0000F,00000 ;DC timing gx145 + ;no pixel double, no interlace, + ;not VGA planar, not FP center, + ;VSYNC active high, HSYNC active high, + ;blink disable, vertical interrupt + ;enable, timing generator disable + ;(will init later), blank enable, + ;HSYNC enable, VSYNC enable, FP + ;power enable + + ;Note: with 5520, sync polarity must + ;be controlled in the 5520 registers - + ;always set active high in GXM. + +;; dw 0830C,03005,00000 ;DC output config gx147 + ;high & low panel data enabled, + ;PCLK enabled, 8 bpp mode +;; dw 08310,00000,00000 ;DC frame buffer start gx148 +;; dw 08314,00280,00000 ;DC comp buffer start gx148 + ;this is within the line for modes + ;<> 1024 bytes per line, after the + ;frame buffer for 1024 column mode. +;; dw 08318,0FE00,00026 ;DC cursor buffer start gx148 +;; dw 08320,00000,00000 ;DC video start offset gx148 + +;#if def GX_720 +; dw 08324,00100,00010 ;DC line delta gx149 +; dw 08328,08252,00000 ;DC buffer size gx149 +; dw 08330,0827F,00317 ;DC h timing gx150 +; dw 08334,0827F,00317 ;DC h blank gx150 +; dw 08338,0828F,002EF ;DC h sync gx151 +; dw 0833C,0828F,002EF ;DC fp h sync gx151 +; dw 08340,0818F,0020C ;DC v timing gx151 +; dw 08344,081B7,001E4 ;DC v blank gx151 +; dw 08348,081C1,001C3 ;DC v sync gx152 +; dw 0834C,081C1,001C3 ;DC fp v sync gx152 +;#endif +;#if def GX_640 ;not tested ! +; dw 08324,00100,00010 ;DC line delta m166 +; dw 08328,08252,00000 ;DC buffer size m166 +; dw 08330,(80-1)*8,(100-1)*8 ;DC h timing m168 active, total +; dw 08334,80*8,98*8 ;DC h blank m168 start, end +; dw 08338,81*8,94*8 ;DC h sync m169 start, end +; dw 0833C,656-1,760-1 ;DC fp h sync m169 start, end +; dw 08340,480-1,525-1 ;DC v timing m169 active, total +; dw 08344,488-1,517-1 ;DC v blank m170 start, end +; dw 08348,490-1,492-1 ;DC v sync m170 start, end +; dw 0834C,490-1,492-1 ;DC fp v sync m170 start, end +;#endif +;; dw 08350,00000,00000 ;DC cursor x gx153 +;; dw 08358,00000,00000 ;DC cursor y gx153 +;; dw 0835C,003FF,00000 ;DC split screen gx153 + +;; dw 08354,000CD,00002 ;DC v line count gx153 RO +;; dw 08378,088A0,01A83 ;DC disp FIFO test gx155 +;; dw 0837C,00000,00000 ;DC comp FIFO test gx155 + +; dw 08500,00000,00000 ;PM SMI status gx182 +; dw 08504,00000,00000 ;PM serial packet control gx183 +; dw 08508,00000,00000 ;PM clock stop control gx183 +; dw 0850C,00000,00000 ;PM serial packet register gx183 RO + +gxtab3e: ;end of table + +#if def GUGUUS + ; + ; GX registers, accessed through CPU_WRITE instruction + ; +gxtab4: dw 0FF0C,0,0 ;BLT buffer 0 base m101 + dw 0FF1C,0,0 ;BLT buffer 1 base m101 + dw 0FF2C,0,0 ;BLT buffer 0 pointer m101 + dw 0FF3C,0,0 ;BLT buffer 1 pointer m101 + dw 0FF6C,0,0 ;Power management base m212 + dw 0FF7C,0,0 ;Power management address mask m212 +gxtab4e: ;end of table +#endif + ; + ; PCI config registers + ; + + ; GX1 registers (north bridge) + +gxtab6: +; dw 00000,01078 ;0000 Vendor ID gx170 RO +; dw 00001 ;0002 Device ID gx170 RO + dw 00004,00107 ;0004 PCI command gx170 + ;enable SERR + dw 00280 ;0006 PCI status gx170 +; dw 00008,00000 ;0008 Revision gx171 RO +; dw 00600 ;0009 Class code gx171 RO + dw 0000C,00000 ;000D PCI latency timer gx171 + dw 00000 + + dw 00040,01c0e ;0040 PCI control function 1 gx171 + ;support multiple PCI write, read, + ;enable slave write buffer, + ;PCI cache line read enable, + ;Xbus burst enable + ;0041 PCI control function 2 gx172 + ;enable Xbus to PCI write buffer + ;1024 byte disconnect boundary + dw 04100 ;0043 PCI arbitration control 1 gx172 + ;grant bus any time; + ;no retry mask; hold X-bus on retries + dw 00044,00000 ;0044 PCI arbitration control 2 gx173 + ;fixed arbitration, REQ0, REQ1, REQ2 + dw 00000 + + ; SC1100 registers (south bridge) + + dw 09004,0000F ;9004 PCI command sc166 + dw 00280 ;9006 PCI status sc167 + dw 0904C,0000F ;904C Top of system memory sc171 + dw 02000 ;512 MB + dw 09050,0447b ;9050 PIT control / ISA divider sc171 + ;PIT reset disable, enable refresh + ;toggle, GATE1 high, enable IRQ0, + ;GATE0 low, ISA clock = PCI / 4 + ;9051 ISA I/O recovery sc172 + ;5 cycles + dw 0021D ;9052 ROM/AT logic control sc172 + ;disable KBC snoop, enable A20M on + ;warm reset, enable port 92, + ;upper ROM use mask register, + ;disable ROM writes, lower ROM 128K + ;9053 Alternate CPU sc173 + ;disable ROMCS for KBD, disable + ;SMI on A20M toggle + dw 0905C,00000 ;905C PCI interrupt map 1 sc174 + ;905D PCI interrupt map 2 sc174 + ;gets programmed later + dw 00000 + dw 09060,00007 ;9060 ACPI control sc175 + dw 00000 + dw 0906C,00000 + dw 0FF00 ;906E ROM mask sc176 -> 16MB + dw 09070,00000 ;9070 IOCS1 base sc176 + dw 00000 ;9072 IOCS1 control sc176 -> disable + dw 09074,00000 ;9074 IOCS0 base sc176 + dw 00000 ;9076 IOCS0 control sc176 -> disable + dw 09078,00000 ;9078 DOCCS base sc177 + dw 00000 + dw 0907C,00000 ;907C DOCCS control sc177 -> disable + dw 00000 + dw 09080,00001 ;9080 PM enable 1 sc177 + ;global enable + ;9081 PM enable 2 sc178 + ;disable idle timers + dw 00000 ;9082 PM enable 3 sc179 + ;disable traps + ;9083 PM enable 4 sc180 + ;disable traps, timers, SMI +; dw 09084,00000 ;9084 PM status 1 shadow sc181 +; ;9085 PM status 2 shadow sc182 +; dw 00000 ;9086 PM status 3 shadow sc182 + ;9087 PM status 4 shadow sc183 + dw 09088,00000 ;9088 GP timer 1 count sc184 + ;9089 GP timer 1 control sc184 + ;timebase 1 s + dw 00000 ;908A GP timer 2 count sc185 + ;908B GP timer 2 control sc185 + ;timebase 1 s + dw 0908C,00000 ;908C IRQ speedup timer count sc186 + dw 00000 + dw 09090,00000 + dw 0c000 ;9093 Misc device control sc186 + dw 09094,00000 ;9094 Suspend modulation OFF count sc186 + ;9095 Suspend modulation ON count sc187 + dw 00000 ;9096 Suspend configuration sc187 + dw 09098,00000 ;9098 HDD idle timer count sc187 + dw 00000 ;909A FDD idle timer count sc187 + dw 0909C,00000 ;909C LPT/COM idle timer count sc188 + dw 00000 ;909E KBD/mouse idle timer count sc188 + dw 090A0,00000 ;90A0 User 1 idle timer count sc188 + dw 00000 ;90A2 User 2 idle timer count sc188 + dw 090A4,00000 ;90A4 User 3 idle timer count sc188 + dw 00000 ;90A6 reserved sc188 +; dw 090AC,00000 ;90AC Sec HDD idle timer count sc188 +; dw 00000 ;90AE CPU suspend command sc189 + ;write will cause suspend ! + ;90AF Suspend notebook command sc189 + ;write will cause suspend ! +; dw 090B4,00004 ;90B4 3F2 shadow sc189 +; ;90B5 3F7 shadow sc189 +; dw 03404 ;90B6 372 shadow sc189 +; ;90B7 377 shadow sc189 +; dw 090B8,01140 ;90B8 8237 shadow sc189 +; ;90B9 8259 shadow sc190 +; dw 0B600 ;90BA 8254 shadow sc190 +; ;90BB RTC index shadow sc190 + dw 090BC,00000 ;90BC Clock stop control sc190 + dw 00000 + dw 090C0,00000 ;90C0 User base 1 sc191 + dw 00000 + dw 090C4,00000 ;90C4 User base 2 sc191 + dw 00000 + dw 090C8,00000 ;90C8 User base 3 sc191 + dw 00000 + dw 090CC,00000 ;90CC User control 1 sc191 + ;90CD User control 2 sc191 + dw 00000 ;90CE User control 3 sc192 +; dw 090D0,00000 ;90D0 Software SMI sc192 +; ;write will cause SMI ! +; dw 00000 +; dw 090EC,00000 ;90EC Timer test sc192 +; dw 00000 +; dw 090F4,00000 ;90F4 PM status 1 sc192 RC +; ;90F5 PM status 2 sc193 RC +; dw 00000 ;90F6 PM status 3 sc194 RC +; ;90F7 PM status 4 sc195 RC + + ; function 1: SMI / ACPI - base already set + + dw 09104,00001 ;9104 PCI command sc207 + dw 00280 ;9106 PCI status sc207 + + ; function 2: IDE + + dw 09204,00005 ;9204 PCI command sc225 + dw 00280 ;9206 PCI status sc225 RO + + ; default timing, set to correct values by cs_ide ! + +#if def FAST_PCI ; 66 MHz fast PCI timing + + dw 09240,0f8e4 ;9240 channel 0 drv 0 PIO sc226 + dw 0f8e4 ;mode 0 default + dw 09244,0fff3 ;9244 channel 0 drv 0 DMA sc227 + dw 0800f ;DMA mode 0, mode format 1 + dw 09248,0f8e4 ;9248 channel 0 drv 1 PIO sc226 + dw 0f8e4 + dw 0924C,0fff3 ;924C channel 0 drv 1 DMA sc227 + dw 0800f + dw 09250,0f8e4 ;9250 channel 1 drv 0 PIO sc226 + dw 0f8e4 + dw 09254,0fff3 ;9254 channel 1 drv 0 DMA sc227 + dw 0800f + dw 09258,0f8e4 ;9258 channel 1 drv 1 PIO sc226 + dw 0f8e4 + dw 0925C,0fff3 ;925C channel 1 drv 1 DMA sc227 + dw 0800f + +#else ; 33 MHz fast PCI timing + + dw 09240,0d132 ;9240 channel 0 drv 0 PIO sc226 + dw 09172 ;mode 0 default + dw 09244,07771 ;9244 channel 0 drv 0 DMA sc227 + dw 08007 ;DMA mode 0, mode format 1 + dw 09248,0d132 ;9248 channel 0 drv 1 PIO sc226 + dw 09172 + dw 0924C,07771 ;924C channel 0 drv 1 DMA sc227 + dw 08007 + dw 09250,0d132 ;9250 channel 1 drv 0 PIO sc226 + dw 09172 + dw 09254,07771 ;9254 channel 1 drv 0 DMA sc227 + dw 08007 + dw 09258,0d132 ;9258 channel 1 drv 1 PIO sc226 + dw 09172 + dw 0925C,07771 ;925C channel 1 drv 1 DMA sc227 + dw 08007 +#endif + + ; function 3: audio (base already set) + + dw 09304,00006 ;9304 PCI command sc231 + dw 00280 ;9306 PCI status sc231 RO + + ; function 4: video processor (base already set) ??? + + dw 09410,00000 ;9410 F4BAR0 video base sc275 (SC1200) + dw 04080 + dw 09414,00000 ;9414 F4BAR1 reserved sc275 (SC1200) + dw 04001 + dw 09418,05000 ;9418 F4BAR2 VIP sc275 (SC1200) + dw 04001 + dw 09404,00002 ;9404 PCI command sc275 + dw 00280 ;9406 PCI status sc275 RO + + dw 0943C,00300+INTC ;943C Interrupt line register sc276 + dw 00000 ;943D Interrupt pin register INTC# sc276 RO + + ; USB / HCI controller (base address already set) + + dw 09804,00007 ;9804 PCI command sc251 + dw 00280 ;9806 PCI status sc251 RO + + dw 0980C,04008 ;980C Cache line size sc252 + ;980D latency timer sc252 + dw 00000 ;980E header type sc252 RO + ;980F BIST sc252 RO + + dw 0983C,00100+INTA ;983C Interrupt line sc253 + ;983D Interrupt pin INTA# sc253 + dw 05000 ;983E Min grant sc253 + ;983F Max latency sc253 +; dw 09840,0,0000F ;9840 ASIC test mode enable sc253 +; dw 09844,0,0 ;9844 ASIC operational mode enable sc253 + +gxtab6e: ;end of table + ; + ; 5540 memory mapped config registers + ; +gxtab9: + ; audio subsystem + + dw AUDBASE+000,00000,00000 ;CODEC GPIO status sc232 + dw AUDBASE+004,00000,00000 ;CODEC GPIO control sc232 + dw AUDBASE+008,00000,00000 ;CODEC status sc232 +; dw AUDBASE+00C,00000,00000 ;CODEC command sc233 +; dw AUDBASE+010,01FFF,01FFF ;L2 audio SMI status sc233 +; dw AUDBASE+014,0FFFF,000FF ;IO trap SMI, fast status sc235 + dw AUDBASE+018,00000,00000 ;IO trap SMI enable sc236 + ;1A internal IRQ -> none + dw AUDBASE+01C,00000,00000 ;Internal IRQ control sc238 + dw AUDBASE+020,00000,00000 ;audio master 0 sc239 + dw AUDBASE+024,00000,00000 ;audio master 0 PRD sc240 + dw AUDBASE+028,00000,00000 ;audio master 1 sc240 + dw AUDBASE+02C,00000,00000 ;audio master 1 PRD sc241 + dw AUDBASE+030,00000,00000 ;audio master 2 sc241 + dw AUDBASE+034,00000,00000 ;audio master 2 PRD sc242 + dw AUDBASE+038,00000,00000 ;audio master 3 sc242 + dw AUDBASE+03C,00000,00000 ;audio master 3 PRD sc243 + dw AUDBASE+040,00000,00000 ;audio master 4 sc243 + dw AUDBASE+044,00000,00000 ;audio master 4 PRD sc244 + dw AUDBASE+048,00000,00000 ;audio master 4 sc244 + dw AUDBASE+04C,00000,00000 ;audio master 4 PRD sc245 +gxtab9e: ;end of table + ; + ; 32 bit I/O mapped registers + ; +gxtab10: dw LPCBASE+000,V_LPC00,00000 ;SERIRQ_SRC serial IRQ source sc200 + dw LPCBASE+004,00000,00000 ;SERIRQ_LVL serial IRQ level sc201 + ;active high + ; this is set with before to preserve LPC flash enable bit ! +; dw LPCBASE+010 ;LAD_EN LPC address enable sc204 +; dd V_LPC10 ;LPC RTC, KBC, wide generic, FDC, + ;COM, LPT + dw LPCBASE+008,00080,00000 ;SERIRQ_CNT serial IRQ control sc203 + ;enable serial IRQ + dw LPCBASE+00C,00014,00000 ;DRQ_SRC DRQ source sc203 + ;disable master cycles, enable DRQ2 + dw LPCBASE+014,V_LPC14,0 ;LAD_D0 LPC address decode 0 sc204 + dw LPCBASE+018,00000,00000 ;LAD_D1 LPC address decode 1 sc204 + ;none + dw LPCBASE+01C,0007F,00000 ;LPC_ERR_SMI LPC error SMI sc205 + ;LPC SMI disable, write 1 to clear stat +; dw LPCBASE+020,00000,00000 ;LPC_ERR_ADD LPC error addr RO sc205 + +; dw XBUSBASE+000,00007,00000 ;I/O control 1 sc250 + ;this is set earlier + dw XBUSBASE+004,00000,00000 ;I/O control 2 sc250 + ;IDSEL: AD28 for chipset, AD29 for USB + ;should write as read + dw XBUSBASE+008,09000,00000 ;I/O control 3 sc250 + ;USB voltage adjustment = default, + ;USB current adjustment = default, + ;disable debug test port + +#if def WRAP2A + dw GPIOBASE+000,00541,00000 ;GPDO0 GPIO out 0 sc196 + ;G2, G3, G18 low = LEDs + ;G1, G17 low = HDCE# + ;G0 high = NANDCE# +#else + dw GPIOBASE+000,00540,00000 ;GPDO0 GPIO out 0 sc196 +; dw GPIOBASE+000,0FFF3,0FFFB ;GPDO0 GPIO out 0 sc196 + ;G2, G3, G18 low +#endif +; dw GPIOBASE+004,00000,00002 ;GPDI0 GPIO in 0 sc196 RO + dw GPIOBASE+008,00000,00000 ;GPIEN0 GPIO int ena 0 sc196 + dw GPIOBASE+00C,0FFFF,00000 ;GPST0 GPIO status 0 sc196 + ;write 1 to clear + dw GPIOBASE+010,0FFFF,0FFFF ;GPDO1 GPIO out 1 sc197 +; dw GPIOBASE+014,00000,00000 ;GPDI1 GPIO in 1 sc197 RO + dw GPIOBASE+018,00000,00000 ;GPIEN1 GPIO int ena 1 sc197 + dw GPIOBASE+01C,0FFFF,00000 ;GPST1 GPIO status 1 sc197 + ;write 1 to clear + dw GPIOBASE+020,00000,00000 ;GPIO cfg select sc197 + dw GPIOBASE+024,00044,00000 ;GPIO pin config sc198 + dw GPIOBASE+028,00000,00000 ;GPIO reset control sc199 +gxtab10e: ;end of table + ; + ; GPIO pin configuration (must be 64 bytes) + ; see GPIO pin config access sc198 for details + ; +gxtab11: +#if def WRAP +#if def WRAP2A + db 003,044,001,001,044,044,044,044 ;G00 + ;G0 output -> NANDCE# + ;G2 output -> LED1# + ;G3 output -> LED2# + db 044,044,044,044,044,044,044,044 ;G08 + ;G14 input -> DOGFOOD (WRAP.2C) + db 004,003,001,004,004,004,004,004 ;G16 + ;G17 output -> HDCE# + ;G18 output -> LED3# + ;G20 input -> NANDBSY# + db 004,004,004,004,004,004,004,004 ;G24 + db 044,044,044,044,044,044,044,044 ;G32 + db 044,044,044,044,044,044,044,047 ;G40 + ;G40 input -> MODESW# + ;G41 input -> read THRM# status + ;G47 output -> PWRBTN# + db 004,004,004,004,004,004,004,004 ;G48 + db 004,004,004,004,004,004,004,004 ;G56 +#else + db 044,044,001,001,044,044,044,044 ;G00 + ;G2 output -> LED1# + ;G3 output -> LED2# + db 044,044,044,044,044,044,044,044 ;G08 + db 004,004,001,004,004,004,004,004 ;G16 + ;G18 output -> LED3# + db 004,004,004,004,004,004,004,004 ;G24 + db 044,044,044,044,044,044,044,044 ;G32 + db 044,044,044,044,044,044,044,047 ;G40 + ;G40 input -> MODESW# + ;G41 input -> read THRM# status + ;G47 output -> PWRBTN# + db 004,004,004,004,004,004,004,004 ;G48 + db 004,004,004,004,004,004,004,004 ;G56 +#endif +#else + db 044,044,044,044,044,044,044,044 ;G00 + db 044,044,044,044,044,044,044,044 ;G08 + db 004,004,004,004,004,004,004,004 ;G16 + db 004,004,004,004,004,004,004,004 ;G24 + db 044,044,044,044,044,044,044,044 ;G32 + db 044,044,044,044,044,044,044,044 ;G40 + db 004,004,004,004,004,004,004,004 ;G48 + db 004,004,004,004,004,004,004,004 ;G56 +#endif + ; SMI status registers (most read only) + +; dw SMIBASE+000,00000 ;L1 PME/SMI status mirror sc208 RO +; dw SMIBASE+002,00000 ;L1 PME/SMI status sc209 RC +; dw SMIBASE+004,00000 ;L2 general traps mirror sc210 RO +; dw SMIBASE+006,00000 ;L2 general traps sc211 RC +; dw SMIBASE+01C,09B65 ;ACPI timer sc212 RO +; dw SMIBASE+01E,0008A ; +; dw SMIBASE+020,00000 ;L2 ACPI status mirror sc212 RO +; dw SMIBASE+022,00000 ;L2 ACPI status sc213 RC +; dw SMIBASE+024,00000 ;External SMI sc213 + + ; ACPI config registers (0 = byte, 1 = word, 2 = longword) + +gxtab12: db 1 + dw ACPIBASE+000,00000 ;P_CNT processor control sc216 + db 0 + dw ACPIBASE+007,000 ;ACPI_FUN_CNT function control sc216 + db 1 + dw ACPIBASE+008,08D31 ;PM1A_STS L1 PME status sc217 + ;(write 1 to clear status) + db 1 + dw ACPIBASE+00A,00000 ;PM1A_EN PME/SCI enable sc218 + db 1 + dw ACPIBASE+00C,00001 ;PM1A_CNT control sc218 + db 0 ;no SMI... + dw ACPIBASE+00E,001 ;ACPI_BIOS_STS sc219 + ;(write 1 to clear) + db 0 + dw ACPIBASE+00F,000 ;ACPI_BIOS_EN sc219 + db 1 + dw ACPIBASE+010,00779 ;GPE0_STS event 0 status sc219 + ;(write 1 to clear) + db 1 + dw ACPIBASE+012,00000 ;GPE0_EN event 0 enable sc221 + db 1 + dw ACPIBASE+014,00000 ;GPWIO control 1,2 sc222 + db 0 + dw ACPIBASE+016,005 ;GPWIO data sc223 + db 2 + dw ACPIBASE+018,00000,00000 ;ACPI SCI_ROUTING sc223 + ;disable +; dw ACPIBASE+01C,0C4E6,0008A ;ACPI timer sc224 RO + + db 0 + dw ACPIBASE+020,000 ;PM2_CNT PM2 control sc224 + + db 0 + dw IDEBASE+000,000 ;IDE bus master 0 command sc229 + db 0 + dw IDEBASE+002,004 ;IDE bus master 0 status sc229 + db 2 + dw IDEBASE+004,00000,00000 ;IDE bus master 0 PRD addr sc229 + db 0 + dw IDEBASE+008,000 ;IDE bus master 1 command sc230 + db 0 + dw IDEBASE+00A,004 ;IDE bus master 1 status sc230 + db 2 + dw IDEBASE+00C,00000,00000 ;IDE bus master 1 PRD addr sc230 +gxtab12e: ;end of table + ; + ; IDE speed detect + ; + ; DL = drive number (80, 81, 82, 83) + ; DS:DI = points to identify buffer + ; +cs_ide: push dx ;save DX + and dx,3 ;mask drive number + shl dx,3 ;* 8 + + ; determine highest PIO mode supported + + mov cl,[di+51*2+1] ;PIO transfer mode 0..2 + test byte [di+53*2],2 ;extra fields valid ? + jz cs_ide5 ;:no + test byte [di+64*2],1 ;mode 3 supported ? + jz cs_ide1 + mov cl,3 ;yes +cs_ide1: test byte [di+64*2],2 ;mode 4 supported ? + jz cs_ide2 + mov cl,4 ;yes +cs_ide2: + ; compare this against the reported PIO timing + + mov ax,[di+68*2] ;min. PIO timing with I/O ready + and ax,ax + jz cs_ide5 + mov ch,4 + mov si,offset cs_idtab1 +cs_ide3: cmp ax,[cs:si] ;compare timing + jbe cs_ide4 ;:done + inc si + inc si + dec ch + jnz cs_ide3 +cs_ide4: cmp cl,ch ;take the lesser mode + jb cs_ide5 + mov cl,ch +cs_ide5: + ; set PIO timing from table + + mov ch,0 + shl cx,2 + mov si,cx + mov eax,[cs:si+cs_idtab2] + mov ebx,080009240 ;PIO register sc226 + add bx,dx ;add drive index + call pci_setd + + ; determine ultra DMA timing + + test byte [di+53*2],4 ;ultra DMA valid ? + jz cs_ide6 ;:no, bail + mov bl,[di+88*2] ;ultra DMA modes supported +#if def FAST_PCI + mov eax,80923261h ;mode 2 + test bl,4 + jnz cs_ide7 ;:yes + mov eax,80933481h ;mode 1 + test bl,2 + jnz cs_ide7 ;:yes + mov eax,809436a1h ;mode 0 +#else + mov eax,80911030h ;mode 2 + test bl,4 + jnz cs_ide7 ;:yes + mov eax,80911140h ;mode 1 + test bl,2 + jnz cs_ide7 ;:yes + mov eax,80921250h ;mode 0 +#endif + test bl,1 + jnz cs_ide7 ;:yes + + ; determine conventional DMA timing + +cs_ide6: mov ax,[di+66*2] ;recommended DMA cycle time + and ax,ax + jz cs_ide9 ;0 -> default to mode 0 + cmp si,120 ;120 ns or less - mode 2 + ja cs_ide9 ;:too slow +#if def FAST_PCI + mov eax,80015151h ;mode 2 timing +#else + mov eax,80002020h ;mode 2 timing +#endif + + ; set DMA timing + +cs_ide7: mov bx,09244 ;DMA register sc226 + add bx,dx ;add drive index + call pci_setd + +cs_ide9: pop dx ;restore drive number + ret + ; + ; PIO cycle times by mode + ; +cs_idtab1: dw 120 ;mode 4 + dw 180 ;mode 3 + dw 240 ;mode 2 + dw 383 ;mode 1 + dw 600 ;mode 0 + ; + ; PIO timing by mode (high -> IDETIM, low -> SIDETIM) + ; +#if def FAST_PCI +cs_idtab2: dw 0f8e4,0f8e4 ;mode 0 - 66 MHz timing + dw 0f353,053f3 ;mode 1 + dw 08141,013f1 ;mode 2 + dw 04231,04231 ;mode 3 + dw 01131,01131 ;mode 4 +#else +cs_idtab2: dw 0d132,09172 ;mode 0 - 33 MHz timing + dw 07121,02171 ;mode 1 + dw 03020,00080 ;mode 2 + dw 02010,02010 ;mode 3 + dw 00010,00010 ;mode 4 +#endif diff --git a/wrap/POST.8 b/wrap/POST.8 new file mode 100644 index 0000000..526ba33 --- /dev/null +++ b/wrap/POST.8 @@ -0,0 +1,448 @@ + ; + ; POST power on self-test + ; + ; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved. + ; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5. + ; + ; pd 040325 extend temporary area (option TEMPSIZE) + ; pd 021118 add disakbd / enakbd around PS/2 mouse detection + ; (suggested by Klaus Pfaadt) + ; pd 010429 change VGA_END to allow for large VGA BIOS (e.g. + ; C&T / Asiliant VGA) + ; pd 991021 add option for M-Systems DiskOnChip + +#if ! def ROM_BEG +ROM_BEG equ 0c800h ;start of ROM scan +#endif + +#if ! def ROM_END +ROM_END equ 0f800h ;end of ROM scan +#endif + +#if ! def VGA_BEG +VGA_BEG equ 0c000h ;start of VGA BIOS scan +#endif + +#if ! def VGA_END +VGA_END equ 0c800h ;end of VGA BIOS scan +#endif + ; + ; Reset entry + ; + ; Note: Processor shutdown is NOT supported. There are easier and + ; faster ways to get out of protected mode. + ; +reset: cli + cld + mov ax,cs ;SS = CS (to support fake stack) + mov ss,ax + mov ds,ax ;DS = CS (for easier table loads) + mov al,01h ;POST code: reset entry + ret_sp postcode + + ret_sp cs_clr ;clear chipset registers to allow + ;access to DMA, IRQ controller + ret_sp post_clr ;clear registers + ; + ; Initialize chipset + ; +resetcs: mov al,02h ;POST code: chipset initialization + ret_sp postcode + ret_sp cs_init ;initialize chipset +rstini: jb rstvid ;:shadow already enabled + ; + ; Detect base memory size + ; + mov al,03h ;POST code: detect base memory size + ret_sp postcode + ret_sp cs_det ;detect memory +rstdet: ;may return by RET or JMP + ; + ; Init shadow RAM - if DRAM is bad, we'll die here + ; (running out of shadow makes for a more effective + ; memory test, and accelerates startup). + ; + mov al,04h ;POST code: initialize shadow RAM + ret_sp postcode + ret_sp cs_shad ;init shadow +rstshad2: + ; + ; Init Hercules video card (blind init, we don't care if it's there) + ; +rstvid: mov al,05h ;POST code: init mono video + ret_sp postcode +#if ! def NO_VIDINIT + ret_sp vid_init ;let there be light ... +#endif + ; + ; disable all PCI adapters on primary bus -> get bus masters + ; to shut up... + ; +#if def PCI +rstpci: mov al,06h ;POST code: disable PCI devices + ret_sp postcode + ret_sp pci_rst +#endif + ; + ; Check low 64KB of DRAM + ; + mov al,07h ;POST code: test low 64KB of DRAM + ret_sp postcode + ret_sp getunreal ;enter unreal mode + xor ebp,ebp ;start address + mov dx,[m_rstflg] ;save reset flag + ret_sp post_t64k ;test first 64K of DRAM + jnb rstmem5 ;:ok + mov al,0f7h ;POST code: low 64KB failure + jmp fatal ;handle fatal error + +rstmem5: mov [m_rstflg],dx ;restore reset flag + ; + ; initialize stack + ; + mov al,08h ;POST code: initialize stack + ret_sp postcode + mov sp,tmp_stack ;set stack + xor ax,ax + mov ss,ax + ; + ; Set CPU specific parameters, enable L1 cache + ; + call cs_cpu ;DS = 0 + ; + ; Check BIOS checksum + ; + mov al,09h ;POST code: BIOS checksum + call postcode + call cs_shadrw ;set read/write shadow + call d_dosum ;update data checksum + call cs_shadro ;set read only shadow +#if ! def NO_ROMSUM + call post_sum ;verify BIOS checksum + call post_err ;hang if error +#endif + ; + ; Configure super I/O + ; + mov al,0ah ;POST code: super I/O initialization + call postcode + call sio_init + ; + ; Clear RTC interrupts, test shutdown byte, set operating mode + ; + mov al,0bh ;POST code: RTC test + call postcode +#if ! def RTC_SKIP + call rtc_test +#if ! def IGNORE_RTC + call post_err +#endif +#endif + ; + ; Test refresh (and indirectly, 8254 timer) + ; + mov al,0ch ;POST code: refresh / 8254 test + call postcode + call post_ref +#if ! def IGNORE_REF + call post_err +#endif + ; + ; Set speed-dependent chipset registers + ; (done after post_ref) + ; + mov al,0dh ;POST code: speed-dependent chipset regs + call postcode + call cs_spd + ; + ; Test 8237 DMA registers + ; + mov al,0eh ;POST code: test 8237 DMA + call postcode +#if ! def DMA_SKIP + call post_dma + call post_err +#endif + ; + ; Test DMA page registers + ; + mov al,0fh ;POST code: test DMA page registers + call postcode + call post_page + call post_err + ; + ; Test 8254 registers + ; + mov al,10h ;POST code: test 8254 registers + call postcode + call post_tim + call post_err + ; + ; Test keyboard controller (don't care whether we have a keyboard) + ; + mov al,11h ;POST code: test keyboard controller + call postcode + call kb_ini +#if ! def NO_KBC + call post_err +#endif + ; + ; Initialize Timer, DMA, interrupt registers, port 92 + ; + mov al,12h ;POST code: init timer, DMA, 8259... + call postcode + call post_tdma + ; + ; Test 8259 interrupt mask registers + ; + mov al,13h ;POST code: test 8259 mask registers + call postcode + call post_irq + call post_err + ; + ; Size and test low 640 KB + ; + mov al,14h ;POST code: test low 640KB + call postcode + call post_base ;base memory test + ; + ; Initialize memory locations, interrupt vectors, etc. + ; + mov al,15h ;POST code: init vectors + call postcode + call post_vec ;init interrupt vectors + call vid_vars ;initialize Hercules video BIOS vars + mov byte [m_devflg],00110000xb ;no floppy present, monochrome + +#if def GX_VID + call gx_video ;initialize GX video +#endif +#if def GX_INT10 + call gxv_init ;initialize GX int 10 +#endif +#if def VID_CGA + call cs_cga ;enable CGA redirect +#endif + ; + ; run PCI plug & play + ; +#if def PCI + mov al,16h ;POST code: PCI plug & play + call postcode + call cs_shadrw ;enable read / write shadow + +#if def PCI_WAIT + mov bx,500 ;option PCI_WAIT: give slow + call cs_waitbx ;PCI peripherals time to wake up +#endif + +#if ! def SKIP_PNP + call pci_pnp + + mov eax,(INTD shl 24)+(INTC shl 16)+(INTB shl 8)+INTA + call cs_pciint ;set interrupt channels +#endif +#endif + ; + ; shadow video BIOS (unless PCI already did it) + ; + mov al,17h ;POST code: shadow video BIOS + call postcode + call cs_vshad + ; + ; Look for VGA video BIOS at C000 + ; + mov al,18h ;POST code: look for VGA BIOS + call postcode + mov bx,VGA_BEG ;starting address + mov dx,VGA_END ;ending address + call post_scan + mov ax,cs + cmp [vec10+2],ax ;did VGA initialize ? + jz rstmda ;:no, still same interrupt + mov byte [m_devflg],0 ;VGA +rstmda: + ; + ; Display signon prompt, base memory size + ; + mov al,19h ;POST code: sign-on prompt + call postcode +#if def CONSOLE + call con_init ;initialize serial console +#endif + mov si,copyrt + call v_msg + xor eax,eax + mov ax,[m_lomem] ;memory size + call post_itoa ;display number + mov si,msg_base ;display " KB Base Memory" + call v_msg + ; + ; keyboard test #2 + ; + mov al,1ah ;POST code: second keyboard test + call postcode + call kb_inb + ; + ; Size and test extended memory + ; + mov al,1bh ;POST code: extended memory test + call postcode +#if ! def SKIP_EXTEST + call cs_a20on ;enable A20 gate + call post_ext ;test extended memory +#endif + ; + ; keyboard test, enable timer tick, enable interrupts + ; + mov al,1ch ;POST code: enable interrupts + call postcode + call kb_inc ;continue keyboard initialization + call tim_init ;initialize timer tick, unmask ints + sti ;enable interrupts + call kb_ind ;set keyboard LEDs + ; + ; test & init RTC + ; + mov al,1dh ;POST code: test / init RTC + call postcode + call rtc_ini + ; + ; Initialize floppy disk: detect, spin up, recalibrate + ; + mov al,1eh ;POST code: init floppy disk + call postcode + call fd_init + ; + ; ROM scan + ; +#if ! def DISKONCHIP + mov al,1fh ;POST code: option ROM scan + call postcode +#if ! def NO_ROMSCAN + mov bx,ROM_BEG ;starting address + mov dx,ROM_END ;ending address + +#if def WRAP ;WRAP: disable PXE BIOS + cmp byte [cs:CFG_OFS+cfg_pxe],0 ;disable ? + jz skiprom +#endif + call post_scan +skiprom: +#endif +#endif + ; + ; Test & init parallel ports + ; + mov al,20h ;POST code: test parallel ports + call postcode + call lp_test + ; + ; Test & init serial ports + ; + mov al,21h ;POST code: test serial ports + call postcode + call rs_test + ; + ; enable numeric coprocessor + ; + mov al,22h ;POST code: enable coprocessor + call postcode + call cs_npx + ; + ; secondary floppy init + ; + mov al,23h ;POST code: floppy init + call postcode + call fd_inb + ; + ; IDE initialization + ; + mov al,24h ;POST code: hard disk init + call postcode + call cs_shadrw ;set read/write shadow + call hd_init ;init disk drives + call d_dosum ;update data checksum + call cs_shadro ;set read only shadow + ; + ; Flash disk initialization + ; +#if def FLASHDISK + call fld_init ;initialize flash disk +#endif + ; + ; ROM scan (alternate location for M-Systems DiskOnChip) + ; + ; Caution: their firmware version 3.3.5 will HANG if no HDD + ; present. Connect a HDD, and update their boot image to + ; DOC121.EXB or later. + ; +#if def DISKONCHIP + mov al,1fh ;POST code: option ROM scan + call postcode +#if ! def NO_ROMSCAN + mov bx,ROM_BEG ;starting address + mov dx,ROM_END ;ending address + call post_scan +#endif +#endif + ; + ; detect PS/2 mouse + ; +#if def PS2MOUSE + mov al,25h ;POST code: PS/2 mouse detect + call postcode + call ps2init ;initialize mouse +#endif + ; + ; Timer / RTC update check + ; + ; Note: This test can take up to one second (normally overlapped + ; with floppy / IDE init), skip if not required. + ; + mov al,26h ;POST code: timer/RTC check + call postcode +#if ! def NO_RTCFAIL + call tim_test + cmp byte [tmp_tim],0 +rsttim: jnz rsttim ;hang if failure +#endif + ; + ; enable L2 cache if present + ; +#if def cs_cache + mov al,27h + call postcode + call cs_cache +#endif + ; + ; OEM decision: verify diagnostic flags to decide + ; whether to boot or display error messages + ; + mov al,28h ;POST code: OEM boot decision point + call postcode + call decide + ; + ; clean up before boot + ; + mov word [m_rstflg],0 ;clear reset flag + mov di,0500h ;clear temporary data area: 0500..11FF +#if ! def TEMPSIZE +TEMPSIZE equ 01200 +#endif + mov cx,(TEMPSIZE-0500h) / 2 + xor ax,ax + rep stosw ;this also overwrites stack ! + ; + ; Boot operating system + ; + mov al,00h ;POST code: boot + call postcode +#if def BOOTBEEP + call beep ;let there be noise +#endif + int 19h ;boot + mov al,0dfh ;we shouldn't get here + call postcode + cli + hlt ;hang diff --git a/wrap/PXE.BIN b/wrap/PXE.BIN new file mode 100644 index 0000000..2ba15ee Binary files /dev/null and b/wrap/PXE.BIN differ diff --git a/wrap/README.TXT b/wrap/README.TXT new file mode 100644 index 0000000..ee7662a --- /dev/null +++ b/wrap/README.TXT @@ -0,0 +1,12 @@ +WRAP BIOS source pd 071129 +---------------- + +Files provided for your reference, use at your own risk. These files +will assemble to the same binary as the 1.11 BIOS version. + +To assemble the BIOS you will need the A386 assembler (www.eji.com). + +wrap1.bat makes wrap1.rom (WRAP.1C..1E) + +wrap2.bat makes wrap2.rom (WRAP.2C..2E) + diff --git a/wrap/SETBIOS.8 b/wrap/SETBIOS.8 new file mode 100644 index 0000000..302fef6 --- /dev/null +++ b/wrap/SETBIOS.8 @@ -0,0 +1,360 @@ + ; + ; WRAP BIOS update + ; + ; pd 030807 + ; pd 040115 compare before erase, retry message on failure + ; + org 0100 + + jmp start + ; + ; variables + ; + even + +rombase equ 0fffe0000 +port61 equ 061 +iowait equ 0eb + +bufpt: dd 0 ;buffer pointer +devid: dw 0 ;device ID +file: dw 0 ;file handle +bufseg: dw 0 ;buffer segment +curseg: dw 0 ;current segment +count: db 4 ;number of 32KB blocks to be read +rom64: db 0 ;1 = 64KB flash + +msg_strt: db "WRAP flash update",13,10 + db "Reading 128KB flash image " +filename: db "wrap.rom",0 ;file name +msg_io: db " - I/O error !",13,10,0 +msg_open: db 13,10,"Flash ID",0 +msg_fail: db " - FAIL !",13,10,0 +msg_zap: db 13,10,"Erase",0 +msg_set: db " Program",0 +msg_cmp: db " Verify",0 +msg_ok: db 13,10,"Flash update OK.",13,10,0 +msg_try: db "(R)etry (Q)uit ?",13,10,0 +msg_same: db 13,10,"Flash verify OK.",13,10,0 + ; + ; main code + ; +start: cld + mov sp,offset stack1 + + mov si,offset msg_strt + call v_msg + + ; set segment values + + xor eax,eax ;set buffer segment + mov ax,cs + shl eax,4 ;-> physical + add eax,offset stack1 + mov [bufpt],eax + shr eax,4 ;-> segment + mov [bufseg],ax + mov [curseg],ax + + xor eax,eax ;set GDT physical address + mov ax,cs + shl eax,4 + add eax,offset gdt + mov dword [gdtadr],eax + + ; open image file + + mov dx,offset filename + mov ax,3d00h ;open file + int 21h + mov [file],ax ;save file handle + jb long ioerr + +start3: mov bx,[file] ;file handle + xor dx,dx ;destination DS:DX + mov ds,[curseg] + mov cx,8000h ;read 32KB + mov ah,03f ;block read + int 21h + push cs ;restore DS + pop ds + jb ioerr + cmp ax,8000h ;read all ? + jnz ioerr + add word [curseg],0800 ;update segment + dec byte [count] + jnz start3 ;:another block + + mov ah,03e ;close + mov bx,[file] ;file handle + int 21h + jb ioerr + +start4: mov si,offset msg_open ;look for flash + call v_msg + call rom_open ;read device ID + + call rom_cmp ;verify flash + mov si,offset msg_same + jnb start9 ;:same, give message and exit + + mov si,offset msg_zap ;erase flash + call v_msg + call rom_zap + + mov si,offset msg_set ;program flash + call v_msg + call rom_set + + mov si,offset msg_cmp ;verify flash + call v_msg + call rom_cmp + mov si,offset msg_ok + jnb start9 ;:ok + + mov si,offset msg_fail +barf: call getreal + call v_msg ;display error message + mov si,offset msg_try + call v_msg ;ask for retry + +start5: mov ah,0 ;get key + int 16h + cmp al,"Q" + jz terminat ;Q = exit + cmp al,"q" + jz terminat + cmp al,"R" + jz start4 ;R = retry + cmp al,"r" + jz start4 + jmp start5 + +ioerr: mov si,offset msg_io ;I/O error +start9: call v_msg ;display message +terminat: call rom_exit ;write-protect flash + mov ax,4c00h ;terminate + int 21h + ; + ; display string [SI] -> TTY + ; +v_msg: cs: lodsb ;get character + and al,al ;0 = end + jz v_msg9 + mov ah,0eh ;TTY output + mov bh,0 ;page 0 + int 10h + jmp v_msg +v_msg9: ret + ; + ; global descriptor table (GDT) for unreal mode + ; + db (($+15) and 0fff0h)-$ dup 0ffh ;even 16 +gdt: dw gdtend-gdt-1 ;GDT limit +gdtadr: dw gdt,000fh ;linear address of GDT + dw 0 + dw 0ffffh,0,9300h,008fh ;4G data segment, accessed +gdtend: + ; + ; Enter unreal (4GB segment) mode -> change DS,ES selector + ; + ; based on code in DDJ 7/90 + ; +getunreal: cli ;disable interrupts + cs: lgdt [gdt] ;load GDT (in data module, writeable) + + mov eax,cr0 + or al,1 ;enable protected mode + mov cr0,eax + jmp short getunrl2 ;flush queue +getunrl2: mov bx,8 ;selector + mov ds,bx + mov es,bx + and al,0feh ;exit protected mode + mov cr0,eax + ret + ; + ; get back to normal segments + ; +getreal: push cs + pop ds + sti + ret + ; + ; open flash access + ; +rom_open: mov eax,80009050h ;9052 enable flash writes sc172 + mov dx,0cf8 + out dx,eax + xchg eax,ebx + mov dl,0fe + in al,dx + or al,2 ;enable flash write + xchg eax,ebx + mov dl,0f8 + out dx,eax + mov dl,0fe + xchg eax,ebx + out dx,al + + ; read device ID + + call getunreal ;enter unreal mode + mov ebx,rombase + mov byte [ebx+05555],0aa ;software ID + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],090 + out iowait,al ;short delay + mov ax,[ebx] ;read the device ID + mov byte [ebx],0f0 ;exit ID mode + call getreal + mov [devid],ax + + ; check device ID + + cmp al,0bf ;SST ? + jnz rom_open9 ;no: fail + cmp ah,0b5 ;39SF010 + jz rom_open8 + cmp ah,0d5 ;39VF010 + jz rom_open8 + inc byte [rom64] ;set flag for 64KB flash + cmp ah,0b4 ;39SF512 + jz rom_open8 + cmp ah,0d4 ;39VF512 + jz rom_open8 + cmp ah,0d6 ;39VF020 + jz rom_open8 + cmp ah,0d7 ;39VF040 + jnz rom_open9 +rom_open8: + ret +rom_open9: + mov si,offset msg_fail + jmp barf + ; + ; close flash access + ; +rom_exit: mov eax,80009050h ;9052 disable flash writes sc172 + mov dx,0cf8 + out dx,eax + xchg eax,ebx + mov dl,0fe + in al,dx + and al,not 2 ;disable flash write + xchg eax,ebx + mov dl,0f8 + out dx,eax + mov dl,0fe + xchg eax,ebx + out dx,al + ret + ; + ; chip erase flash + ; +rom_zap: call getunreal ;enter unreal mode + mov ebx,rombase + mov byte [ebx+05555],0aa ;erase setup + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],080 + mov byte [ebx+05555],0aa ;erase command + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],010 + + mov bx,100 ;wait for 100 ms + call cs_waitbx + + mov eax,0ffffffff ;check for erase + mov ecx,08000 ;128KB + mov edi,rombase + a4 repz scasd + jnz rom_open9 ;:failure + jmp getreal + ; + ; program flash [bufpt] -> [rombase] + ; +rom_set: mov esi,[bufpt] + mov edi,rombase + mov ecx,020000 ;128KB + cmp byte [rom64],1 ;64KB flash ? + jnz rom_set2 + shr ecx,1 ;yes + add esi,ecx + add edi,ecx +rom_set2: call getunreal + mov ebx,edi ;^base + +rom_set3: mov al,[esi] ;data byte + cmp al,0ff + jz rom_set5 ;blank -> skip + + mov byte [ebx+05555],0aa ;byte program command + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],0a0 + mov [edi],al + + mov dx,1000 ;time-out +rom_set4: cmp [edi],al ;correct data ? + jz rom_set5 ;:yes + dec dx + jnz rom_set4 + jmp rom_open9 ;program failure, bail + +rom_set5: inc esi + inc edi + dec ecx + jnz rom_set3 ;:another + jmp getreal + ; + ; compare flash [bufpt] -> [rombase], carry set if mismatch + ; +rom_cmp: mov esi,[bufpt] + mov edi,rombase + mov ecx,020000 ;128KB + cmp byte [rom64],1 ;64KB flash ? + jnz rom_cmp2 + shr ecx,1 ;yes + add esi,ecx + add edi,ecx +rom_cmp2: call getunreal + shr ecx,2 +rom_cmp3: mov eax,[esi] + cmp [edi],eax + jnz rom_cmp4 ;:failure + lea esi,[esi+4] + lea edi,[edi+4] + dec ecx + jnz rom_cmp3 + call getreal + clc + ret + +rom_cmp4: call getreal ;failure exit + stc + ret + ; + ; Wait BX milliseconds - depends on refresh rate !!! + ; + ; This is used for floppy delays and INT15 function 86. + ; +cs_waitbx: inc bx + jmp short cs_wbx8 + +cs_wbx1: mov cx,62 ;62 refresh cycles per millisecond +cs_wbx2: in al,port61 + and al,10h + mov ah,al +cs_wbx3: in al,port61 ;wait for refresh bit to change state + and al,10h + cmp al,ah + jz cs_wbx3 + loop cs_wbx2 ;:another iteration +cs_wbx8: dec bx ;another millisecond ? + jnz cs_wbx1 +cs_wbx9: ret + ; + ; stack + ; + db (($+100) and 0fff0h)-$ dup 0ffh ;allocate stack +stack1: ;this is end, buffer follows... + ;buffer is even 16 diff --git a/wrap/SETBIOS.COM b/wrap/SETBIOS.COM new file mode 100644 index 0000000..a781310 Binary files /dev/null and b/wrap/SETBIOS.COM differ diff --git a/wrap/SIO.8 b/wrap/SIO.8 new file mode 100644 index 0000000..486c014 --- /dev/null +++ b/wrap/SIO.8 @@ -0,0 +1,265 @@ + ; + ; Initialize SC1100 super I/O + ; + ; (C)2000-2003 Pascal Dornier / PC Engines; All rights reserved. + ; + ; pd 050227 change serial port init -> DTR, RTS set + ; disable COM2 IR UART + +SIO_IDX equ 002eh ;SC1100 SIO +AB1BASE equ 0810h ;Access.Bus 1 base +AB2BASE equ 0820h ;Access.Bus 2 base +SWCBASE equ 0860h ;power control base + +#if def DISA_SIO +sio_act equ 0 ;don't activate +#else +sio_act equ 1 ;activate device +#endif + ; + ; early SIO initialization (jumped to by cs_clr) + ; +sio_init0: + mov dx,offset SIO_IDX ;SC1100 SIO base + mov al,20h ;SID register sc75 RO + out dx,al + inc dx + in al,dx + dec dx + mov si,offset sio_tab0 ;table + cmp al,0f5h ;verify device ID + jz sio_ini01 ;:ok +#if ! def DISA_SIO +;&&& jmp cs_clr ;loop back if config failed +#endif +sio_ini01: cs: lodsw ;get table entry + out dx,al ;index + inc dx + mov al,ah + out dx,al ;data + dec dx + cmp si,offset sio_tab0e ;end of table ? + jb sio_ini01 ;:no + +#if !def DISA_SIO + + ; configure RTC + + mov al,0dh + out cm_idx,al + in al,cm_dat + in al,cm_dat + + ; set SWC registers (I/O mapped) from table + + mov si,offset swctab + mov dx,swcbase +sio_swc: cs: lodsb + out dx,al + inc dx + cmp si,offset swctab9 + jb sio_swc + +#if def CONSOLE + + ; init COM for 9600 8N1 + + mov dx,CONSOLE+3 + mov al,80h ;DLAB=1: access baud rate register + out dx,al + + mov al,0 ;9600 baud + mov dl,low(CONSOLE+1) + out dx,al + +#if def CFG_BAUD + mov al,byte [cs:CFG_OFS+cfg_baud] ;get baud rate + cmp al,3 ;38400 baud + jz sio_baud + cmp al,2 ;57600 baud + jz sio_baud + cmp al,12 + jz sio_baud + mov al,CONRATE ;set default rate otherwise +sio_baud: +#else + mov al,CONRATE ;set default rate +#endif + mov dl,low(CONSOLE) + out dx,al + mov al,COM_INIT and 1Fh ;set parameters + mov dl,low(CONSOLE+3) + out dx,al + + mov al,0 ;disable interrupts + mov dl,low(CONSOLE+1) + out dx,al + + mov al,3 ;enable RTS, DTR + mov dl,low(CONSOLE+4) + out dx,al +#endif +#endif + +#if def sio2_init + jmp sio2_init ;init second SIO +#else + ret +#endif + ; + ; late SIO initialization + ; +sio_init: ret + ; + ; NS SC1100 SIO configuration + ; +sio_tab0: ;db 020,0F5 ;SID register sc73 RO + db 021,001 ;SIOCF1 config sc73 + ;global enable +; db 022,002 ;SIOCF2 config sc73 +; db 027,001 ;SRID revision sc75 R + + db 007,000 ;RTC + db 060,000 ;base MSB + db 061,070 ;base LSB + db 062,000 ;ext base MSB + db 063,072 ;ext base LSB + db 070,008 ;RTC int + db 071,000 ;RTC int type + db 0F0,000 ;RLR RAM lock sc75 + ;no effect + db 0F1,000 ;DOMAO month alarm offset sc75 + db 0F2,000 ;MAO month alarm offset sc75 + db 0F3,000 ;CENO century offset sc75 + db 030,000 ;disable + db 030,sio_act ;enable + + db 007,005 ;Access.bus 1 + db 060,high(AB1BASE) ;Base MSB + db 061,low(AB1BASE) ;Base LSB + db 070,000 ;AC1 int + db 071,003 ;AC1 int type + db 0F0,004 ;AC1 config sc78 + ;enable internal pu + db 030,000 ;disable + db 030,001 ;enable + + db 007,006 ;Access.bus 2 + db 060,high(AB2BASE) ;Base MSB + db 061,low(AB2BASE) ;Base LSB + db 070,000 ;AC2 int + db 071,003 ;AC2 int type + db 0F0,004 ;AC2 config sc78 + ;enable internal pu + db 030,000 ;disable + db 030,001 ;enable + + db 007,001 ;SWC system wake-up control + db 060,high(SWCBASE) ;Base MSB + db 061,low(SWCBASE) ;Base LSB + db 070,000 ;int + db 071,003 ;int type + db 030,sio_act ;disable + + db 007,008 ;COM1 + db 060,003 ;Base MSB + db 061,0F8 ;Base LSB + db 070,004 ;COM1 int + db 071,003 ;COM1 int type + db 0F0,082 ;COM1 config sc79 + ;normal power, enable bank switch + db 030,sio_act ;Activate COM1 + + db 007,002 ;COM2/IR + db 060,002 ;Base MSB + db 061,0F8 ;Base LSB + db 070,003 ;IR int + db 071,003 ;IR int type + db 074,004 ;IR RX_DMA + db 075,004 ;IR TX_DMA + db 0F0,082 ;IR config sc77 + ;normal power, enable bank switch + db 030,0 ;Activate IR -> disable + +sio_tab0e: ;end of table + ; + ; SWC init table + ; +swctab: db 000,000 ;00 WKSR wake status sc94 + db 001,000 ;01 WKC wake control sc94 + db 002,001 ;02 WKCFG wake config sc94 -> bank 1 +; db 003,000 ;03 IRWCR CEIR wake control sc95 +; db 005,000 ;05 IRWAD wake address sc95 + db 006,000 ;06 IRWAM wake address mask sc95 +; db 007,000 ;07 ADSR address shift sc95 +; db 008,000 ;08 IRWTR0L wake range low sc95 +; db 009,000 ;09 IRWTR0H wake range high sc95 +; db 00A,000 ;0A IRWTR1L wake range low sc96 +; db 00B,000 ;0B IRWTR1H wake range high sc96 +; db 00C,000 ;0C IRWTR2L wake range low sc96 +; db 00D,000 ;0D IRWTR2H wake range high sc96 +; db 00E,000 ;0E IRWTR3L wake range low sc96 +; db 00F,000 ;0F IRWTR3H wake range high sc96 +swctab9: + ; + ; serial port console + ; +#if def CONSOLE + +con_init: cmp byte [cs:CFG_OFS+cfg_cons],0 ;console disable ? + jz con_init9 + + ; second entry, unconditional enable + +con_init2: + xor ax,ax + mov ds,ax + + ; hook serial interrupt + +#if def CONINT + cli ;set INT 3 vector = COM2 +#if CONINT = 3 + mov word [11*4],con_int + mov word [11*4+2],cs +#endif +#if CONINT = 4 + mov word [12*4],con_int + mov word [12*4+2],cs +#endif +#if CONINT = 5 + mov word [13*4],con_int + mov word [13*4+2],cs +#endif + sti + + mov dx,CONSOLE + in al,dx ;clear receive buffer + out iowait,ax + mov dl,low(CONSOLE+1) + mov al,1 ;enable receive interrupt + out dx,al + out iowait,ax + mov dl,low(CONSOLE+4) ;enable interrupt driver + in al,dx + or al,8 + out dx,al + + in al,pic0+1 ;enable serial interrupt (int 3) +#if CONINT = 3 + and al,0f7 +#endif +#if CONINT = 4 + and al,0ef +#endif +#if CONINT = 5 + and al,0df +#endif + out iowait,ax + out pic0+1,al +#endif + mov byte [m_conkey],0 ;clear key buffer + mov byte [m_console],1 ;set serial console flag +con_init9: + ret +#endif diff --git a/wrap/SMI.BIN b/wrap/SMI.BIN new file mode 100644 index 0000000..51f31ad Binary files /dev/null and b/wrap/SMI.BIN differ diff --git a/wrap/WRAP.8 b/wrap/WRAP.8 new file mode 100644 index 0000000..a7d366d --- /dev/null +++ b/wrap/WRAP.8 @@ -0,0 +1,240 @@ + ; + ; TinyBIOS, configured for National SC1100, PC Engines WRAP board + ; + ; (C)1998-2003 Pascal Dornier / PC Engines; All rights reserved. + ; + ; v1.11 + ; pd 051108 extend PCI reset pulse width + ; + ; v1.10 + ; pd 050625 add option for slower DRAM clock + ; + ; v1.08 + ; pd 050502 be less picky about IDE device IDs + ; + ; v1.07 + ; pd 050417 add delay on warm start (protect CF integrity) + ; add serial console disable option + ; + ; v1.06 + ; pd 050227 fix serial port configuration; neutral message on + ; WRAP setup; add USB, PXE configuration option; + ; Int 15 function E820 memory map. + ; Disable COM2 IR UART. + ; + ; v1.05 + ; pd 041021 add platform ID at end + ; + ; v1.03 + ; pd 040705 Configure latency + line size on ALL slots (incl. 68). + ; + ; v1.02 + ; pd 040315 Add PCI reset on reset entry. + ; + ; v1.01 + ; pd 040220 Change to generic message, leave space for patch + ; pd 040220 Add USB_EN option. + +; start offset + +STARTOFS equ 09000 ;start offset, must be multiple of 256 +CFG_OFS equ 08000 ;start of config block + + db STARTOFS dup 0 ;(start data cut out by BIOSSUM.EXE) + +#if def CFG_OFS + include wrap_cfg.8 ;include config offsets +#endif + +; platform specific options + +WRAP1C: ;select new interrupt mapping + +WRAP: ;select WRAP platform + +;USB_EN: ;enable USB controller -> now configured + ;by cfg_usb + +ROM_BEG equ 0c800 ;start of option ROM scan +ROM_END equ 0f000 ;end of option ROM scan + +PCI_NORST equ 090 ;don't reset 5530 +PCI_NORST2 equ 098 ;don't reset USB +PCI_NORST3 equ 000 ;don't reset GX1 + +; PCI options + +INTA equ 9 ;PCI interrupt assignment +INTB equ 10 ;also see PCI_TAB +INTC equ 11 +INTD equ 12 +INT0 equ 0ff ;no interrupt assigned + +; general options + +;BOOTBEEP: ;enables beep on bootup + +;DEBUG: ;debug mode (Int 13 trace etc). + +;QUICKMEM: ;no memory test (fill only) +MTEST_1PASS: ;quicker memory test (1 pass) + +; hard disk, boot options + +FORCE_LBA equ 1024 ;use LBA if more than x cylinders + +;BOOT_AC: ;Boot A: first, then C: + ;comment out for C: then A: + +;HD_WAIT equ 20 ;Hard disk wait, max. x seconds +;HD_ENA equ 0 ;don't check HDD status before x seconds + +HD_INFO: ;display hard disk info + +HDD_LOOSE: ;don't be picky about device ID + +HDD_EDD: ;enable EDD support + +HDD_LBA: ;enable LBA support + +HDD_NOSLAVE: ;don't look (and wait) for slave device + +;HD_TIME equ 080 ;commented out = HDD power down disabled + ;0 = code included, but no timeout + ;1..240 = timeout x * 5 s units + ;241..251 = timeout (x-240)*30 min + +; keyboard options + +NO_KBC: ;don't fail if KBC not present + +INT15_24: ;option: A20 gate functions + +;LED_UPDATE: ;Define to enable keyboard LED updates + ;(NumLock, CapsLock, ScrollLock). + ;Not recommended for real-time apps. + +; serial port options + +CONSOLE equ 03F8 ;serial port for console = COM1 +CONRATE equ 12 ;default baudrate + ;3 = 38400, 12 = 9600 - override by + ;CFG_BAUD +CONINT equ 4 ;interrupt for console +COM_NO2400: ;prevent DOS 2400 baud setting... + + ; + ; Signon prompt (must be at start of binary, 4KB step, + ; for patch utility to work) + ; +copysig: db "##" ; +copysum: db 0 ;checksum +copyrt: +#if def WRAP1C + db "PC Engines WRAP.1C/1D/1E v1.11",13,10,0 +#else + db "PC Engines WRAP.1A/1B v1.11",13,10,0 +#endif + db copysig+256-$ dup (0) ;pad to 256 byte limit + + include ..\message.8 + ; + ; Include files + ; + include ..\equ.8 ;general equates + include gxm.8 ;SC1200 chipset / system specific code + include sio.8 ;super I/O initialization + include post.8 ;POST (with local modifications) + include ..\post2.8 ;POST routines + include ..\debug.8 ;Debug routines, comment out + include ..\vid.8 ;video BIOS + include ..\int1x.8 ;miscellaneous interrupts + include ..\fdd.8 ;floppy BIOS + include ..\hdd.8 ;hard disk BIOS + include ..\com.8 ;serial BIOS + include ..\kbd.8 ;keyboard BIOS + include ..\kbtab.8 ;keyboard table + include ..\lpt.8 ;printer BIOS + include ..\rtc.8 ;timer / RTC BIOS + include ..\pci.8 ;PCI BIOS + include ..\pcipnp.8 ;PCI plug & play +#if def PS2MOUSE + include ..\ps2mous.8 ;PS/2 mouse BIOS +#endif +#if def CFG_OFS + include wrap_set.8 ;WRAP setup module + include wrap_xm.8 ;WRAP xmodem module +#endif + ; + ; OEM decision: verify diagnostic flags to decide + ; whether to boot or display error messages + ; +decide: mov dx,GPIOBASE+0 ;GPDO0 GPIO out 0 sc196 + in eax,dx + or eax,040008 ;turn off LED2/G3 and LED3/G18 + out dx,eax + + mov dx,LPCBASE+010 ;LAD_EN LPC address enable sc204 + in eax,dx + and eax,07FFF ;disable LPC flash + out dx,eax + + mov ax,04008 ;PCI latency, cache line size + mov ebx,08000680C ;set for miniPCI + LAN +decide2: call pci_setw ;pd 040705 fix + inc bh + cmp bh,090 + jb decide2 + +#if def wrap_set + call wrap_set +#endif + ret + ; + ; BIOS writeable configuration data + ; + include ..\data.8 + ; + ; INT 1A legacy entry point + ; + db (0fe6e-$) dup 0ffh ;explicitly documented in the + jmp int1a ;PCI BIOS spec. + ; + ; PCI interrupt assignment table + ; +PCI_TAB: db INT0,INT0,INT0,INT0 ;00 north bridge + db INT0,INT0,INT0,INT0 ;08 + db INT0,INT0,INT0,INT0 ;10 + db INT0,INT0,INT0,INT0 ;18 + db INT0,INT0,INT0,INT0 ;20 + db INT0,INT0,INT0,INT0 ;28 + db INT0,INT0,INT0,INT0 ;30 + db INT0,INT0,INT0,INT0 ;38 + db INT0,INT0,INT0,INT0 ;40 + db INT0,INT0,INT0,INT0 ;48 + db INT0,INT0,INT0,INT0 ;50 + db INT0,INT0,INT0,INT0 ;58 + db INT0,INT0,INT0,INT0 ;60 +#if def WRAP1C + db INTD,INTA,INT0,INT0 ;68 AD23 miniPCI J6 + db INTB,INT0,INT0,INT0 ;70 AD24 LAN U13 + db INTA,INT0,INT0,INT0 ;78 AD25 LAN U12 + db INTC,INT0,INT0,INT0 ;80 AD26 LAN U11 + db INTA,INTD,INT0,INT0 ;88 AD27 miniPCI J5 +#else + db INT0,INT0,INT0,INT0 ;68 + db INTD,INTA,INT0,INT0 ;70 AD24 miniPCI J6 + db INTB,INT0,INT0,INT0 ;78 AD25 LAN U12 + db INTC,INT0,INT0,INT0 ;80 AD26 LAN U11 + db INTA,INTD,INT0,INT0 ;88 AD27 miniPCI J5 +#endif + db INTA,INTB,INTC,INTD ;90 AD28 south bridge + db INTA,INTB,INTC,INTD ;98 AD29 USB + db INT0,INT0,INT0,INT0 ;remaining devices +pci_tab9: ;end of table + include ..\tables.8 ;ISA initialization tables + + safeorg 0ffd0 + db "PC Engines WRAP.1",0 + + include ..\reset.8 ;reset vector diff --git a/wrap/WRAP2.8 b/wrap/WRAP2.8 new file mode 100644 index 0000000..aa4885f --- /dev/null +++ b/wrap/WRAP2.8 @@ -0,0 +1,241 @@ + ; + ; TinyBIOS, configured for National SC1100, PC Engines WRAP board + ; + ; (C)1998-2003 Pascal Dornier / PC Engines; All rights reserved. + ; + ; v1.11 + ; pd 051108 extend PCI reset pulse width + ; + ; v1.10 + ; pd 050625 add option for slower DRAM clock + ; + ; v1.09 + ; pd 050521 allow USB also for WRAP.2C (will add on WRAP.2D) + ; some customers use this for timing. + ; + ; v1.08 + ; pd 050502 be less picky about IDE device IDs + ; disable IR UART + ; + ; v1.07 + ; pd 050417 add delay on warm start (protect CF integrity) + ; add serial console disable option + ; + ; v1.06 + ; pd 050227 fix serial port configuration; neutral message on + ; WRAP setup; add PXE configuration option; + ; Int 15 function E820 memory map. + ; disable COM2 IR UART. + ; + ; v1.05 + ; pd 041021 add platform ID at end of flash + ; pd 041011 fix PMR setting for GPIO14 (DOGFOOD pin) + ; + ; v1.04 + ; pd 040817 add support for 39VF020/39VF040 flash + ; + ; v1.03 + ; pd 040705 Configure latency + line size on ALL slots (incl. 68). + ; + +; start offset + +STARTOFS equ 09000 ;start offset, must be multiple of 256 +CFG_OFS equ 08000 ;start of config block + + db STARTOFS dup 0 ;(start data cut out by BIOSSUM.EXE) + +#if def CFG_OFS + include wrap_cfg.8 ;include config offsets +#endif + +; platform specific options + +;&&&PCI_WAIT: ;add PCI startup delay + +WRAP2A: ;select new interrupt mapping + +WRAP: ;select WRAP platform + +ROM_BEG equ 0c800 ;start of option ROM scan +ROM_END equ 0f000 ;end of option ROM scan + +PCI_NORST equ 090 ;don't reset 5530 +PCI_NORST2 equ 098 ;don't reset USB +PCI_NORST3 equ 000 ;don't reset GX1 + +; PCI options + +INTA equ 9 ;PCI interrupt assignment +INTB equ 10 ;also see PCI_TAB +INTC equ 11 +INTD equ 12 +INT0 equ 0ff ;no interrupt assigned + +; general options + +;BOOTBEEP: ;enables beep on bootup + +;DEBUG: ;debug mode (Int 13 trace etc). + +;QUICKMEM: ;no memory test (fill only) +MTEST_1PASS: ;quicker memory test (1 pass) + +; hard disk, boot options + +FORCE_LBA equ 1024 ;use LBA if more than x cylinders + +;BOOT_AC: ;Boot A: first, then C: + ;comment out for C: then A: + +;HD_WAIT equ 20 ;Hard disk wait, max. x seconds +;HD_ENA equ 0 ;don't check HDD status before x seconds + +HD_INFO: ;display hard disk info + +HDD_LOOSE: ;don't be picky about device ID + +HDD_EDD: ;enable EDD support + +HDD_LBA: ;enable LBA support + +HDD_NOSLAVE: ;don't look (and wait) for slave device + +;HD_TIME equ 080 ;commented out = HDD power down disabled + ;0 = code included, but no timeout + ;1..240 = timeout x * 5 s units + ;241..251 = timeout (x-240)*30 min + +; keyboard options + +NO_KBC: ;don't fail if KBC not present + +INT15_24: ;option: A20 gate functions + +;LED_UPDATE: ;Define to enable keyboard LED updates + ;(NumLock, CapsLock, ScrollLock). + ;Not recommended for real-time apps. + +; serial port options + +CONSOLE equ 03F8 ;serial port for console = COM1 +CONRATE equ 12 ;default baudrate + ;3 = 38400, 12 = 9600 - override by + ;CFG_BAUD +CONINT equ 4 ;interrupt for console +COM_NO2400: ;prevent DOS 2400 baud setting... + + ; + ; Signon prompt (must be at start of binary, 4KB step, + ; for patch utility to work) + ; +copysig: db "##" ; +copysum: db 0 ;checksum +copyrt: db "PC Engines WRAP.2B/2C v1.11",13,10,0 + db copysig+256-$ dup (0) ;pad to 256 byte limit + + include ..\message.8 + ; + ; Include files + ; + include ..\equ.8 ;general equates + include gxm.8 ;SC1200 chipset / system specific code +#if def DRAMOPT + include sdramopt.8 ;SC1200 SDRAM timing optimization + include i2c.8 ;SC1200 I2C routines +#endif + include sio.8 ;super I/O initialization + include post.8 ;POST / local modifications + include ..\post2.8 ;POST routines + include ..\debug.8 ;Debug routines, comment out + include ..\vid.8 ;video BIOS + include ..\int1x.8 ;miscellaneous interrupts + include ..\fdd.8 ;floppy BIOS + include ..\hdd.8 ;hard disk BIOS + include ..\com.8 ;serial BIOS + include ..\kbd.8 ;keyboard BIOS + include ..\kbtab.8 ;keyboard table + include ..\lpt.8 ;printer BIOS + include ..\rtc.8 ;timer / RTC BIOS + include ..\pci.8 ;PCI BIOS + include ..\pcipnp.8 ;PCI plug & play +#if def PS2MOUSE + include ..\ps2mous.8 ;PS/2 mouse BIOS +#endif +#if def CFG_OFS + include wrap_set.8 ;WRAP setup module + include wrap_xm.8 ;WRAP xmodem module +#endif + ; + ; OEM decision: verify diagnostic flags to decide + ; whether to boot or display error messages + ; +decide: mov dx,GPIOBASE+0 ;GPDO0 GPIO out 0 sc196 + in eax,dx + or eax,040008 ;turn off LED2/G3 and LED3/G18 + out dx,eax + + mov dx,LPCBASE+010 ;LAD_EN LPC address enable sc204 + in eax,dx + and eax,07FFF ;disable LPC flash + out dx,eax + + mov ax,04008 ;PCI latency, cache line size + mov ebx,08000680C ;set for miniPCI + LAN +decide2: call pci_setw ;pd 040705 fix + inc bh + cmp bh,090 + jb decide2 + +#if def wrap_set + call wrap_set +#endif + ret + ; + ; BIOS writeable configuration data + ; + include ..\data.8 + ; + ; INT 1A legacy entry point + ; + db (0fe6e-$) dup 0ffh ;explicitly documented in the + jmp int1a ;PCI BIOS spec. + ; + ; PCI interrupt assignment table + ; +PCI_TAB: db INT0,INT0,INT0,INT0 ;00 north bridge + db INT0,INT0,INT0,INT0 ;08 + db INT0,INT0,INT0,INT0 ;10 + db INT0,INT0,INT0,INT0 ;18 + db INT0,INT0,INT0,INT0 ;20 + db INT0,INT0,INT0,INT0 ;28 + db INT0,INT0,INT0,INT0 ;30 + db INT0,INT0,INT0,INT0 ;38 + db INT0,INT0,INT0,INT0 ;40 + db INT0,INT0,INT0,INT0 ;48 + db INT0,INT0,INT0,INT0 ;50 + db INT0,INT0,INT0,INT0 ;58 + db INT0,INT0,INT0,INT0 ;60 +#if def WRAP2A + db INTD,INTA,INT0,INT0 ;68 AD23 miniPCI J6 + db INTB,INT0,INT0,INT0 ;70 AD24 LAN U13 + db INTA,INT0,INT0,INT0 ;78 AD25 LAN U12 + db INTC,INT0,INT0,INT0 ;80 AD26 LAN U11 + db INTA,INTD,INT0,INT0 ;88 AD27 miniPCI J5 +#else + db INT0,INT0,INT0,INT0 ;68 + db INTD,INTA,INT0,INT0 ;70 AD24 miniPCI J6 + db INTB,INT0,INT0,INT0 ;78 AD25 LAN U12 + db INTC,INT0,INT0,INT0 ;80 AD26 LAN U11 + db INTA,INTD,INT0,INT0 ;88 AD27 miniPCI J5 +#endif + db INTA,INTB,INTC,INTD ;90 AD28 south bridge + db INTA,INTB,INTC,INTD ;98 AD29 USB + db INT0,INT0,INT0,INT0 ;remaining devices +pci_tab9: ;end of table + include ..\tables.8 ;ISA initialization tables + + safeorg 0ffd0 + db "PC Engines WRAP.2",0 + + include ..\reset.8 ;reset vector diff --git a/wrap/WRAP2.BAT b/wrap/WRAP2.BAT new file mode 100644 index 0000000..bc1bc6b --- /dev/null +++ b/wrap/WRAP2.BAT @@ -0,0 +1,4 @@ +a386 wrap_cfg.8 wrap_cfg.bin +a386 wrap2.8 wrap2.bin +..\tool\biossum wrap2.bin wrap2.abs +..\tool\cat pxe.bin ..\fill32.bin ..\fill32.bin wrap_cfg.bin wrap2.abs > wrap2.rom diff --git a/wrap/WRAP_CFG.8 b/wrap/WRAP_CFG.8 new file mode 100644 index 0000000..6d0554a --- /dev/null +++ b/wrap/WRAP_CFG.8 @@ -0,0 +1,39 @@ + ; + ; WRAP configuration block, rewritten by setup module + ; + ; pd 050625 added cfg_dram + ; pd 050417 added cfg_cons + ; pd 030807 initial code + ; +cfg_baud equ 0 ;offset for baud rate +cfg_lba equ 2 ;offset for LBA / CHS selection +cfg_usb equ 4 ;offset for USB enable / disable +cfg_pxe equ 6 ;offset for PXE enable / disable +cfg_cons equ 8 ;offset for console enable / disable +cfg_dram equ 10 ;offset for DRAM speed config + +cfg_size equ 12 ;size of config block (dword multiple) + +#if ! def CFG_OFS ;set CFG_OFS in BIOS main file ! + + org cfg_baud + dw 3 ;3 = 38400, 12 = 9600, 2 = 57600 + + org cfg_lba ;default to 9600 baud if other value + dw 0 ;1..1024 = threshold for LBA mode + ;default to CHS if other value + + org cfg_usb ;default to disable + dw 0 ;0000 = disable, FFFF = enable + + org cfg_pxe ;default to disable + dw 0 ;0000 = disable, FFFF = enable + + org cfg_cons ;default to enable + dw 0ffff ;0000 = disable, FFFF = enable + + org cfg_dram ;default to 2.5x clock divider + dw 0 ;0000 = 2.5x, FFFF = 3.0x + + db (01000-$) dup (0ff) ;pad the rest of this block +#endif diff --git a/wrap/WRAP_SET.8 b/wrap/WRAP_SET.8 new file mode 100644 index 0000000..40e35d7 --- /dev/null +++ b/wrap/WRAP_SET.8 @@ -0,0 +1,443 @@ + ; + ; WRAP setup module - called by DECIDE + ; + ; This currently supports SST 39VF010 / 39VF512. + ; + ; pd 050625 add DRAM config + ; pd 050521 allow USB for WRAP.2C + ; pd 050417 add CONS option + ; pd 050227 change to neutral message, add USB & PXE options + ; pd 040817 add 39VF020 (not tested) / 39VF040 support + ; pd 040713 add network boot option (N key) + ; pd 040511 add 57600 baud option + ; +wrap_set: xor ax,ax ;copy config block from flash to + mov ds,ax ;temp buffer + mov es,ax + mov si,cfg_ofs + mov di,tmp_buf +ws_copy: cs: movsw + cmp di,tmp_buf+cfg_size + jb ws_copy + + ; if serial console disabled: look for pushbutton + +#if def WRAP1C + test byte [tmp_buf+cfg_cons],1 + jnz wrap_se1 ;:normal console mode + mov dx,GPIOBASE+20 ;check button + in ax,dx + test ax,0100h ;bit 8 = 0 ? + jnz wrap_se1 ;:not pressed + call con_init2 ;enable serial console + jmp wrap_se2 ;enter setup +#endif + +wrap_se1: mov ah,1 ;any key in buffer ? + int 16h + jnz ws_flush ;yes - eat them, see if setup mode + ret + +net_boot: int 18h ;call network boot first + ret ;(not installed: message) + +ws_flush: call ws_getc ;get a key, convert to upper case + cmp al,"N" ;network boot ? + jz net_boot ;:yes + cmp al,"S" ;setup ? + jnz wrap_se1 ;no: eat + +wrap_se2: mov si,offset ws_msgin ;display start message + call v_msg + +ws_main: call ws_menu ;display menu + call ws_getc ;get keystroke + cmp al,"9" ;9600 baud ? + jz ws_96 + cmp al,"3" ;38400 baud ? + jz ws_38 + cmp al,"5" ;57600 baud ? + jz ws_56 + cmp al,"L" ;LBA mode ? + jz ws_lba + cmp al,"C" ;CHS mode ? + jz ws_chs + cmp al,"U" ;USB toggle ? + jz ws_usb + cmp al,"D" ;DRAM toggle ? + jz ws_dram + cmp al,"E" ;PXE toggle ? + jz ws_pxe + cmp al,"R" ;console toggle ? + jz ws_cons + cmp al,"X" ;XMODEM upload ? + jz ws_xmo + cmp al,"Q" ;exit ? + jnz ws_main ;no: loop back + + mov si,offset ws_msgyn + cmp word [tmp_buf+cfg_cons],0 + jnz ws_ex0 + mov si,offset ws_msgr2 ;display serial console warning +ws_ex0: call v_msg +ws_ex1: call ws_getc ;get keystroke + cmp al,"N" + jz ws_rst ;no -> exit immediately + cmp al,"Y" + jnz ws_ex1 + call ws_prog ;write config data to flash + ; + ; force a system reset + ; +ws_rst: mov ebx,080009044 ;reset control sc170 + mov al,0f ;system wide reset + call pci_setb +ws_rst2: hlt ;wait for reset to kick in + jmp ws_rst2 + ; + ; set 9600 baud mode + ; +ws_96: mov word [tmp_buf+cfg_baud],12 + jmp ws_main + ; + ; set 38400 baud mode + ; +ws_38: mov word [tmp_buf+cfg_baud],3 + jmp ws_main + ; + ; set 57600 baud mode + ; +ws_56: mov word [tmp_buf+cfg_baud],2 + jmp ws_main + ; + ; set LBA mode + ; +ws_lba: mov word [tmp_buf+cfg_lba],1 + jmp ws_main + ; + ; set CHS mode + ; +ws_chs: mov word [tmp_buf+cfg_lba],0 + jmp ws_main + ; + ; toggle USB mode + ; +ws_usb: not word [tmp_buf+cfg_usb] + jmp ws_main + ; + ; toggle DRAM mode + ; +ws_dram: not word [tmp_buf+cfg_dram] + jmp ws_main + ; + ; toggle serial console mode + ; +ws_cons: not word [tmp_buf+cfg_cons] + jmp ws_main + ; + ; toggle PXE mode + ; +ws_pxe: not word [tmp_buf+cfg_pxe] + jmp ws_main + ; + ; XMODEM upload + ; +ws_xmo: xor ax,ax ;&&& + mov ds,ax ;&&& + mov dword [xm_pt],010000000 ;destination 1000:0000 + call xm_rec ;receive file + jb ws_main ;:error + mov ax,01000h ;check header + mov es,ax + cmp word [es:0],'BT' ;signature: TB + jnz ws_main ;no: don't execute + les di,[xm_pt] ;get pointer to end of upload + ;(for length verification) + call 01000:0002 ;execute uploaded code + jmp ws_main ;back to menu + ; + ; display menu + ; +ws_menu: mov ax,"9" ;not 9600 baud + mov bl,[tmp_buf+cfg_baud] + cmp bl,3 ;38400 baud ? + jz ws_menu2 + cmp bl,2 ;57600 baud ? + jz ws_menu2 + mov ah,0ff ;9600 baud default +ws_menu2: mov si,offset ws_msg96 + push ax ;save status + call ws_item ;display item + pop ax + + mov ax,"3" + cmp byte [tmp_buf+cfg_baud],3 + jnz ws_menu2a + not ah +ws_menu2a: + mov si,offset ws_msg38 + call ws_item + + mov ax,"5" + cmp byte [tmp_buf+cfg_baud],2 + jnz ws_menu2b + not ah +ws_menu2b: + mov si,offset ws_msg56 + call ws_item + + mov ax,[tmp_buf+cfg_lba] + and ax,ax + jz ws_menu3 + mov ah,0ff ;LBA mode active +ws_menu3: push ax ;save for LBA + not ah ;CHS mode first + mov al,"C" + mov si,offset ws_msgch + call ws_item + pop ax + + mov al,"L" ;LBA item + mov si,offset ws_msglb + call ws_item + + mov ax,[tmp_buf+cfg_pxe] + mov al,"E" ;PXE item + mov si,offset ws_msgpx + call ws_item + + mov ax,[tmp_buf+cfg_usb] + mov al,"U" ;USB item + mov si,offset ws_msgu1 + call ws_item + + mov ax,[tmp_buf+cfg_dram] + mov al,"D" ;DRAM item + mov si,offset ws_msgd1 + call ws_item + + mov ax,[tmp_buf+cfg_cons] + mov al,"R" ;serial console + mov si,offset ws_msgr1 + call ws_item + + mov ax,"X" + mov si,offset ws_msgup + call ws_item + + mov ax,"Q" + mov si,offset ws_msgex ;fall through + ; + ; display menu item AL = character, AH = 00 -> (), AH = FF -> [ ] + ; SI = text + ; +ws_item: push ax + mov al,"(" + cmp ah,0 + jz ws_item2 + mov al,"*" ;highlight active with * +ws_item2: call putc + pop ax + push ax + call putc ;display character of menu item + pop ax + mov al,")" + cmp ah,0 + jz ws_item3 + mov al,"*" ;highlight active with * +ws_item3: call putc + jmp v_msg ;display string + ; + ; get a keystroke, convert to upper case + ; +ws_getc: mov ah,0 + int 16h + cmp al,"a" + jb ws_getc2 + cmp al,"z" + ja ws_getc2 + sub al,20h +ws_getc2: ret + ; + ; Texts + ; +ws_msgin: db 10,"BIOS setup:",13,10,10,0 +ws_msg96: db " 9600 baud ",0 +ws_msg38: db " 38400 baud ",0 +ws_msg56: db " 57600 baud",13,10,0 +ws_msgch: db " CHS mode ",0 +ws_msglb: db " LBA mode ",13,10,0 +ws_msgu1: db " USB enable ",13,10,0 +ws_msgr1: db " Serial console enable ",13,10,0 +ws_msgd1: db " Conservative DRAM timing ",13,10,0 +ws_msgpx: db " Etherboot enable ",13,10,0 +ws_msgup: db " Xmodem upload ",0 +ws_msgex: db " Quit",13,10,0 +#if def WRAP1C +ws_msgr2: db 10,"Warning: Serial console disable selected.",13,10 + db "To re-enable serial console and enter setup, ",13,10 + db "keep switch pressed during power up.",13,10 +#else +ws_msgr2: db 13,10,10,"WARNING: Serial console disable selected.",13,10 + db "The only way to re-enable serial console will be to",13,10 + db "boot from CF card and reprogram the BIOS !",13,10 +#endif + ; message continued ! + +ws_msgyn: db "Save changes Y/N ?",0 +ws_msgpg: db 13,10,"Writing setup to flash... ",0 +ws_msgok: db "OK",13,10,0 +ws_msgfa: db "FAIL",13,10,0 + +cfg_128k equ 0fffe0000 ;128KB ROM base +cfg_rom equ 0ffff0000 ;64KB ROM base +cfg_phys equ cfg_rom+CFG_OFS ;physical address of config block +xm_base equ tmp_buf+cfg_size ;base for xmodem variables + ; + ; program flash config block + ; + ; note: DS / ES changed to unreal mode + ; +ws_prog: mov si,offset ws_msgpg + call v_msg + + call rom_open ;look for flash + jb ws_prog8 ;:error + + mov edi,cfg_phys + call rom_era ;erase block + jb ws_prog8 ;:error + + mov esi,tmp_buf + mov edi,cfg_phys + mov ecx,cfg_size + call rom_set ;program data + jb ws_prog8 ;:error + + mov esi,tmp_buf + mov edi,cfg_phys + mov cx,cfg_size / 4 + call rom_cmp ;verify data + mov si,offset ws_msgok + jnb ws_prog9 +ws_prog8: mov si,offset ws_msgfa ;fail message +ws_prog9: call v_msg + jmp rom_exit ;close flash access + ; + ; open flash access + ; +rom_open: mov ebx,80009052h ;9052 enable flash writes sc172 + call pci_getb + or al,2 ;enable flash write + call pci_setb + + ; read device ID + + call getunreal ;enter unreal mode + mov ebx,cfg_rom + mov byte [ebx+05555],0aa ;software ID + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],090 + out iowait,al ;short delay + mov ax,[ebx] ;read the device ID + mov byte [ebx],0f0 ;exit ID mode + + cmp ax,0d5bf ;39VF010 ? + jz rom_open9 ;yes: ok + cmp ax,0d4bf ;39VF512 ? + jz rom_open9 ;yes: ok + cmp ax,0d6bf ;39VF020 ? + jz rom_open9 ;yes: ok + cmp ax,0d7bf ;39VF040 ? + jz rom_open9 +rom_open8: stc ;error exit +rom_open9: ret + ; + ; erase setup + ; +rom_clr: call getunreal + mov ebx,cfg_128k + mov byte [edi+05555],0aa ;erase setup + mov byte [edi+02aaa],055 + mov byte [edi+05555],080 + mov byte [edi+05555],0aa ;erase command + mov byte [edi+02aaa],055 + ret + ; + ; chip erase flash + ; +rom_zap: call rom_clr ;erase setup + mov edi,ebx ;(cfg_128k) + mov byte [edi+05555],010 ;chip erase command + mov bx,100 ;wait for 100 ms + call cs_waitbx + mov ecx,08000 ;128KB +rom_zap2: xor eax,eax ;check for erase + dec eax + a4 repz scasd + jnz rom_open8 ;:failure + clc + ret + ; + ; block erase flash [edi] + ; +rom_era: call rom_clr ;erase setup + mov byte [edi],030 ;sector erase command @ sector address + mov bx,25 ;wait for 25 ms + call cs_waitbx + mov ecx,0400 ;4 KB + jmp rom_zap2 ;check for erase + ; + ; program flash [esi] -> [edi] (ecx bytes) + ; +rom_set: call getunreal + mov ebx,cfg_128k ;base for command registers + +rom_set3: mov al,[esi] ;data byte + cmp al,0ff + jz rom_set5 ;blank -> skip + + mov byte [ebx+05555],0aa ;byte program command + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],0a0 + mov [edi],al + + mov dx,1000 ;time-out +rom_set4: cmp [edi],al ;correct data ? + jz rom_set5 ;:yes + dec dx + jnz rom_set4 + jmp rom_open9 ;program failure, bail + +rom_set5: inc esi + inc edi + dec ecx + jnz rom_set3 ;:another + clc ;ok exit + ret + ; + ; compare flash [esi],[edi] (cx dwords) + ; + ; must be dword multiples + ; +rom_cmp: call getunreal +rom_cmp2: mov eax,[esi] + cmp [edi],eax + jnz rom_open8 ;:failure + lea esi,[esi+4] + lea edi,[edi+4] + dec cx + jnz rom_cmp2 + clc + ret ;ok exit + ; + ; close flash access + ; +rom_exit: mov eax,80009052h ;9052 disable flash writes sc172 + call pci_getb + and al,not 2 ;disable flash write + call pci_setb + xor ax,ax ;restore segments + mov ds,ax + mov es,ax + ret diff --git a/wrap/WRAP_UP.8 b/wrap/WRAP_UP.8 new file mode 100644 index 0000000..8d7f753 --- /dev/null +++ b/wrap/WRAP_UP.8 @@ -0,0 +1,332 @@ + ; + ; WRAP BIOS update - for Xmodem upload + ; + ; pd 030807, 030812 + ; pd 050625 add 39VF040 + ; +rombase equ 0fffe0000 +CONSOLE equ 03f8 +port61 equ 061 +iowait equ 0eb + + org 0 + + db "TB" ;signature + +entry: jmp start + ; + ; output AL to serial port + ; +xm_out: push dx + push ax + mov dx,CONSOLE+5 +xm_out1: in al,dx ;wait for transmit ready + out iowait,ax + test al,40h + jz xm_out1 + mov dl,low(CONSOLE) + pop ax + out dx,al + pop dx + ret + ; + ; display message [cs:si] + ; +v_msg1: call xm_out +v_msg: cs: lodsb + cmp al,0 + jnz v_msg1 + ret + ; + ; variables + ; + even + +bufpt: dd 0 ;buffer pointer +devid: dw 0 ;device ID +file: dw 0 ;file handle +bufseg: dw 0 ;buffer segment +curseg: dw 0 ;current segment +count: db 4 ;number of 32KB blocks to be read +rom64: db 0 ;1 = 64KB flash + +msg_strt: db "WRAP flash update",13,10,0 +msg_len: db "Invalid image length",13,10,0 +msg_open: db "Flash ID",0 +msg_fail: db " - FAIL !",13,10,0 +msg_zap: db 13,10,"Erase",0 +msg_set: db " Program",0 +msg_cmp: db " Verify",0 +msg_ok: db 13,10,"Flash updated successfully.",13,10,0 + ; + ; main code + ; +start: cld + push cs + pop ds + + mov si,offset msg_strt + call v_msg + + mov si,offset msg_len + mov ax,es ;verify image length + cmp ax,03000 + jnz barf ;:bad + cmp di,buf + jb barf + + ; set segment values + + xor eax,eax ;set buffer segment + mov ax,cs + shl eax,4 ;-> physical + add eax,offset buf + mov [bufpt],eax + shr eax,4 ;-> segment + mov [bufseg],ax + mov [curseg],ax + + xor eax,eax ;set GDT physical address + mov ax,cs + shl eax,4 + add eax,offset gdt + mov dword [gdtadr],eax + + mov si,offset msg_open ;look for flash + call v_msg + call rom_open ;read device ID + jb barf + + mov si,offset msg_zap ;erase flash + call v_msg + call rom_zap + jb barf + + mov si,offset msg_set ;program flash + call v_msg + call rom_set + jb barf + + mov si,offset msg_cmp ;verify flash + call v_msg + call rom_cmp + jb barf + + mov si,offset msg_ok ;success message +barf: call v_msg + call rom_exit ;write-protect flash + xor ax,ax ;restore DS,ES + mov ds,ax + mov es,ax + retf ;return to setup + ; + ; global descriptor table (GDT) for unreal mode + ; + db (($+15) and 0fff0h)-$ dup 0ffh ;even 16 +gdt: dw gdtend-gdt-1 ;GDT limit +gdtadr: dw gdt,000fh ;linear address of GDT + dw 0 + dw 0ffffh,0,9300h,008fh ;4G data segment, accessed +gdtend: + ; + ; Enter unreal (4GB segment) mode -> change DS,ES selector + ; + ; based on code in DDJ 7/90 + ; +getunreal: cli ;disable interrupts + cs: lgdt [gdt] ;load GDT (in data module, writeable) + + mov eax,cr0 + or al,1 ;enable protected mode + mov cr0,eax + jmp short getunrl2 ;flush queue +getunrl2: mov bx,8 ;selector + mov ds,bx + mov es,bx + and al,0feh ;exit protected mode + mov cr0,eax + ret + ; + ; get back to normal segments + ; +getreal: push cs + pop ds + sti + ret + ; + ; open flash access + ; +rom_open: mov eax,80009050h ;9052 enable flash writes sc172 + mov dx,0cf8 + out dx,eax + xchg eax,ebx + mov dl,0fe + in al,dx + or al,2 ;enable flash write + xchg eax,ebx + mov dl,0f8 + out dx,eax + mov dl,0fe + xchg eax,ebx + out dx,al + + ; read device ID + + call getunreal ;enter unreal mode + mov ebx,rombase + mov byte [ebx+05555],0aa ;software ID + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],090 + out iowait,al ;short delay + mov ax,[ebx] ;read the device ID + mov byte [ebx],0f0 ;exit ID mode + call getreal + mov [devid],ax + + ; check device ID + + cmp al,0bf ;SST ? + jnz rom_open9 ;no: fail + cmp ah,0d6 ;39VF020 + jz rom_open8 + cmp ah,0d7 ;39VF040 + jz rom_open8 + cmp ah,0b5 ;39SF010 + jz rom_open8 + cmp ah,0d5 ;39VF010 + jz rom_open8 + inc byte [rom64] ;set flag for 64KB flash + cmp ah,0b4 ;39SF512 + jz rom_open8 + cmp ah,0d4 ;39VF512 + jnz rom_open9 +rom_open8: + clc + ret +rom_open9: + stc + mov si,offset msg_fail + ret + ; + ; close flash access + ; +rom_exit: mov eax,80009050h ;9052 disable flash writes sc172 + mov dx,0cf8 + out dx,eax + xchg eax,ebx + mov dl,0fe + in al,dx + and al,not 2 ;enable flash write + xchg eax,ebx + mov dl,0f8 + out dx,eax + mov dl,0fe + xchg eax,ebx + out dx,al + ret + ; + ; chip erase flash + ; +rom_zap: call getunreal ;enter unreal mode + mov ebx,rombase + mov byte [ebx+05555],0aa ;erase setup + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],080 + mov byte [ebx+05555],0aa ;erase command + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],010 + + mov bx,100 ;wait for 100 ms + call cs_waitbx + + mov eax,0ffffffff ;check for erase + mov ecx,08000 ;128KB + mov edi,rombase + a4 repz scasd + jnz rom_open9 ;:failure + call getreal + clc + ret + ; + ; program flash [bufpt] -> [rombase] + ; +rom_set: mov esi,[bufpt] + mov edi,rombase + mov ecx,020000 ;128KB + cmp byte [rom64],1 ;64KB flash ? + jnz rom_set2 + shr ecx,1 ;yes + add esi,ecx + add edi,ecx +rom_set2: call getunreal + mov ebx,edi ;^base + +rom_set3: mov al,[esi] ;data byte + cmp al,0ff + jz rom_set5 ;blank -> skip + + mov byte [ebx+05555],0aa ;byte program command + mov byte [ebx+02aaa],055 + mov byte [ebx+05555],0a0 + mov [edi],al + + mov dx,1000 ;time-out +rom_set4: cmp [edi],al ;correct data ? + jz rom_set5 ;:yes + dec dx + jnz rom_set4 + jmp rom_open9 ;program failure, bail + +rom_set5: inc esi + inc edi + dec ecx + jnz rom_set3 ;:another + call getreal + clc + ret + ; + ; compare flash [bufpt] -> [rombase] + ; +rom_cmp: mov esi,[bufpt] + mov edi,rombase + mov ecx,020000 ;128KB + cmp byte [rom64],1 ;64KB flash ? + jnz rom_cmp2 + shr ecx,1 ;yes + add esi,ecx + add edi,ecx +rom_cmp2: call getunreal + shr ecx,2 + mov eax,[esi] + cmp [edi],eax + jnz rom_open9 ;:failure + lea esi,[esi+4] + lea edi,[edi+4] + dec ecx + jnz rom_cmp2 + call getreal + clc + ret + ; + ; Wait BX milliseconds - depends on refresh rate !!! + ; +cs_waitbx: inc bx + jmp short cs_wbx8 + +cs_wbx1: mov cx,62 ;62 refresh cycles per millisecond +cs_wbx2: in al,port61 + and al,10h + mov ah,al +cs_wbx3: in al,port61 ;wait for refresh bit to change state + and al,10h + cmp al,ah + jz cs_wbx3 + loop cs_wbx2 ;:another iteration +cs_wbx8: dec bx ;another millisecond ? + jnz cs_wbx1 +cs_wbx9: ret + ; + ; flash image is appended here + ; + db (($+15) and 0fff0h)-$ dup 0ffh ;even 16 +buf: ;flash image follows here diff --git a/wrap/WRAP_XM.8 b/wrap/WRAP_XM.8 new file mode 100644 index 0000000..b3e36b8 --- /dev/null +++ b/wrap/WRAP_XM.8 @@ -0,0 +1,298 @@ + ; + ; Xmodem protocol code, derived from Georges Menie / www.menie.org + ; released under LGPL license + ; + ; Limitations: Only supports CRC mode. Destination offset must be + ; multiple of 1K. + ; + ; (C)2003 Pascal Dornier / PC Engines GmbH + ; + ; pd 030812 initial code + + ; messages + +xm_msgst: db "Start XMODEM transmission... ",0 +xm_msgok: db 13,10,"OK",13,10,0 +xm_msgsy: db 13,10,"Sync err",13,10,0 +xm_msgca: db 13,10,"Cancelled",13,10,0 +xm_msgre: db 13,10,"Too many retries",13,10,0 + + ; XMODEM variables + +xm_pt equ xm_base ;data destination +xm_pktsz equ xm_base+4 ;packet size (bytes) +xm_sum equ xm_base+6 ;CRC accumulator +xm_rxcrc equ xm_base+8 ;received CRC +xm_rxno equ xm_base+10 ;received packet number (norm + inv) +xm_cnt equ xm_base+12 ;byte counter +xm_pktno equ xm_base+14 ;expected packet number +xm_retran equ xm_base+15 ;retransmission counter +xm_retry equ xm_base+16 ;retry counter +xm_trych equ xm_base+17 ;trial character + ; + ; input AL from serial port, time-out CX (32 us each) + ; +xm_in: mov cx,31000 ;about 1 second +xm_in0: mov dx,CONSOLE+5 +xm_in1: in al,dx ;receive data ready ? + test al,1 + jnz xm_in9 ;:yes + +xm_in2: in al,port61 ;wait for refresh bit clear + test al,10h + jnz xm_in2 + +xm_in3: in al,port61 ;wait for refresh bit set + test al,10h + jz xm_in3 + loop xm_in1 + mov al,0 ;clear character + stc ;time-out + ret + +xm_in9: mov dx,CONSOLE ;receive data + in al,dx + clc ;ok exit + ret + ; + ; output AL to serial port + ; +xm_out: push dx + push ax + mov dx,CONSOLE+5 +xm_out1: in al,dx ;wait for transmit ready + out iowait,ax + test al,40h + jz xm_out1 + mov dl,low(CONSOLE) + pop ax + out dx,al + pop dx + ret + ; + ; flush input + ; +xm_flush: mov cx,500 ;short time-out + call xm_in0 + jnb xm_flush ;eat data + ret +; +; Copyright 2001, 2002 Georges Menie (www.menie.org) +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU Lesser General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +SOH equ 001 +STX equ 002 +EOT equ 004 +ACK equ 006 +NAK equ 015 +CAN equ 018 +XM_MAXRETRY equ 25 ;max number retries + ; + ; receive file by Xmodem protocol -> [xm_pt] + ; +xm_rec: mov si,offset xm_msgst ;display start message + call v_msg + + mov dx,CONSOLE+1 ;disable serial interrupt + mov al,0 + out dx,al + + mov byte [xm_pktno],1 + mov byte [xm_retran],xm_maxretry + mov byte [xm_trych],'C' + + ; main loop + +xm_rec0: mov byte [xm_retry],16 ;for( retry = 0; retry < 16; ++retry) { +xm_rec1: dec byte [xm_retry] + jz xm_rec6 ;:end of retries + + mov al,[xm_trych] ; if (trychar) _outbyte(trychar); + cmp al,0 + jz xm_rec2 + call xm_out +xm_rec2: mov cx,65000 ;if ((c = _inbyte((DLY_1S)<<1)) >= 0) { + call xm_in0 + jb xm_rec1 ;:time-out + cmp al,SOH ;switch (c) { + jnz xm_rec3 ;case SOH: + mov word [xm_pktsz],128 ; bufsz = 128; + jmp xm_rec10 ; goto start_recv; + +xm_rec3: cmp al,STX ;case STX: + jnz xm_rec4 + mov word [xm_pktsz],1024 ; bufsz = 1024; + jmp xm_rec10 ; goto start_recv; + +xm_rec4: cmp al,EOT ;case EOT: + jnz xm_rec5 + call xm_flush; ; flushinput(); + mov al,ACK ; _outbyte(ACK); + call xm_out + mov si,offset xm_msgok ;ok message + clc +xm_end: pushf + call v_msg + mov dx,CONSOLE+1 ;enable serial interrupt + mov al,1 + out dx,al + popf + ret + +xm_rec5: cmp al,CAN ;case CAN: + jnz xm_rec1 + call xm_in ;if ((c = _inbyte(DLY_1S)) == CAN) { + cmp al,CAN + jnz xm_rec1 + call xm_flush ; flushinput(); + mov al,ACK ; _outbyte(ACK); + call xm_out + mov si,offset xm_msgca ; return -1; /* canceled by remote */ +xm_err: stc ;error flag + jmp xm_end ;error exit + +xm_rec6: ; we don't support checksum mode, too risky for firmware update ! + + call xm_flush ;flushinput(); + mov al,CAN ;_outbyte(CAN); + call xm_out + call xm_out ;_outbyte(CAN); + call xm_out ;_outbyte(CAN); + mov si,offset xm_msgsy ;sync error + jmp xm_err + + ; receive a packet +xm_rec10: ;start_recv: + mov word [xm_sum],0 ;clear CRC sum + mov byte [xm_trych],0 ;trychar = 0; + les di,[xm_pt] ;p = xbuff; + + call xm_in ;get packet number + jb xm_rec19 ;:reject + mov [xm_rxno],al + call xm_in ;get inverted packet number + jb xm_rec19 ;:reject + mov [xm_rxno+1],al + + mov ax,[xm_pktsz] ;get packet data + mov [xm_cnt],ax +xm_rec11: call xm_in ;-> [es:di] + jb xm_rec19 ;:reject + stosb + call xm_crc ;accumulate CRC + dec word [xm_cnt] + jnz xm_rec11 + + call xm_in ;get CRC high + jb xm_rec19 ;:reject + mov [xm_rxcrc+1],al + call xm_in ;get CRC low +xm_rec19: jb xm_rec20 ;:reject + mov [xm_rxcrc],al + + ; validate packet + + mov ax,[xm_rxcrc] ;compare CRC + cmp ax,[xm_sum] + jnz xm_rec20 ;:no + + mov ax,[xm_rxno] ;received packet number + not ah + cmp al,ah ;match non-inverted ? + jnz xm_rec20 ;:no + + cmp al,[xm_pktno] ;compare with expected packet number + jz xm_rec12 ;:ok + inc ax ;expected packet number - 1 ? + cmp al,[xm_pktno] + jnz xm_rec20 ;no: bad + jmp xm_rec14 ;send ACK, but don't update pointer + +xm_rec12: mov [xm_pt],di ;good packet - update pointer + mov ax,es + cmp di,0 ;end of segment ? + jnz xm_rec13 + add ax,1000h ;next segment + mov [xm_pt+2],ax ;update segment +xm_rec13: inc byte [xm_pktno] ;increment expected packet number + mov byte [xm_retran],XM_MAXRETRY + +xm_rec14: dec byte [xm_retran] + jz xm_rec15 ;:too many retries + mov al,ACK + call xm_out + jmp xm_rec0 ;continue main loop, clear retry cntr + +xm_rec15: call xm_flush + mov al,CAN + call xm_out + call xm_out + call xm_out + mov si,offset xm_msgre ;too many retries + jmp xm_err + +xm_rec20: call xm_flush ;time-out during packet + mov al,NAK + call xm_out + jmp xm_rec1 + ; + ; update CRC - AL = data byte, [xm_crc] = CRC accumulator + ; +xm_crc: mov bx,[xm_sum] + xor al,bh + shl bx,8 + mov ah,0 + mov si,offset xm_crctab + add si,ax + add si,ax + xor bx,[cs:si] + mov [xm_sum],bx + ret + + even +xm_crctab: + dw 00000,01021,02042,03063,04084,050a5,060c6,070e7 + dw 08108,09129,0a14a,0b16b,0c18c,0d1ad,0e1ce,0f1ef + dw 01231,00210,03273,02252,052b5,04294,072f7,062d6 + dw 09339,08318,0b37b,0a35a,0d3bd,0c39c,0f3ff,0e3de + dw 02462,03443,00420,01401,064e6,074c7,044a4,05485 + dw 0a56a,0b54b,08528,09509,0e5ee,0f5cf,0c5ac,0d58d + dw 03653,02672,01611,00630,076d7,066f6,05695,046b4 + dw 0b75b,0a77a,09719,08738,0f7df,0e7fe,0d79d,0c7bc + dw 048c4,058e5,06886,078a7,00840,01861,02802,03823 + dw 0c9cc,0d9ed,0e98e,0f9af,08948,09969,0a90a,0b92b + dw 05af5,04ad4,07ab7,06a96,01a71,00a50,03a33,02a12 + dw 0dbfd,0cbdc,0fbbf,0eb9e,09b79,08b58,0bb3b,0ab1a + dw 06ca6,07c87,04ce4,05cc5,02c22,03c03,00c60,01c41 + dw 0edae,0fd8f,0cdec,0ddcd,0ad2a,0bd0b,08d68,09d49 + dw 07e97,06eb6,05ed5,04ef4,03e13,02e32,01e51,00e70 + dw 0ff9f,0efbe,0dfdd,0cffc,0bf1b,0af3a,09f59,08f78 + dw 09188,081a9,0b1ca,0a1eb,0d10c,0c12d,0f14e,0e16f + dw 01080,000a1,030c2,020e3,05004,04025,07046,06067 + dw 083b9,09398,0a3fb,0b3da,0c33d,0d31c,0e37f,0f35e + dw 002b1,01290,022f3,032d2,04235,05214,06277,07256 + dw 0b5ea,0a5cb,095a8,08589,0f56e,0e54f,0d52c,0c50d + dw 034e2,024c3,014a0,00481,07466,06447,05424,04405 + dw 0a7db,0b7fa,08799,097b8,0e75f,0f77e,0c71d,0d73c + dw 026d3,036f2,00691,016b0,06657,07676,04615,05634 + dw 0d94c,0c96d,0f90e,0e92f,099c8,089e9,0b98a,0a9ab + dw 05844,04865,07806,06827,018c0,008e1,03882,028a3 + dw 0cb7d,0db5c,0eb3f,0fb1e,08bf9,09bd8,0abbb,0bb9a + dw 04a75,05a54,06a37,07a16,00af1,01ad0,02ab3,03a92 + dw 0fd2e,0ed0f,0dd6c,0cd4d,0bdaa,0ad8b,09de8,08dc9 + dw 07c26,06c07,05c64,04c45,03ca2,02c83,01ce0,00cc1 + dw 0ef1f,0ff3e,0cf5d,0df7c,0af9b,0bfba,08fd9,09ff8 + dw 06e17,07e36,04e55,05e74,02e93,03eb2,00ed1,01ef0 diff --git a/wrap/logo/PUTLOGO.EXE b/wrap/logo/PUTLOGO.EXE new file mode 100644 index 0000000..64f05f5 Binary files /dev/null and b/wrap/logo/PUTLOGO.EXE differ diff --git a/wrap/logo/PUTLOGO.PAS b/wrap/logo/PUTLOGO.PAS new file mode 100644 index 0000000..77b6a20 --- /dev/null +++ b/wrap/logo/PUTLOGO.PAS @@ -0,0 +1,97 @@ +{$i-,r-,s-} + +{ paste user logo file into BIOS binary + + Text structure: + + ## signature (2 bytes, at multiple of 4096) + + checksum (1 byte, to avoid messing up main checksum) + + text (253 bytes, terminated / padded by 0) + + Compiler: Borland Pascal 7.0 + + History: + + pd 040220 initial code +} + +const + biosstart=$18000; + sig1=ord('#'); + sig2=ord('#'); + +var + fi:file; + fn:string; + i,base:word; + newsum,oldsum:byte; + buf:array[0..32767] of byte; + newtext:array[0..252] of byte; + +procedure iochk; +begin + if ioresult<>0 then begin + writeln('I/O error: ',fn); + halt(1); + end; +end; + +begin + if paramcount<>2 then begin + write('Usage: '); + halt(1); + end; + + { read new text file } + + assign(fi,paramstr(2)); + reset(fi,1); iochk; + fillchar(newtext,sizeof(newtext),0); + blockread(fi,newtext,sizeof(newtext),i); iochk; + close(fi); iochk; + newsum:=0; + for i:=0 to 252 do + inc(newsum,newtext[i]); + + { open ROM file } + + assign(fi,paramstr(1)); + reset(fi,1); iochk; + seek(fi,biosstart); iochk; + blockread(fi,buf,sizeof(buf)); iochk; + write('BIOS date: '); + for i:=$7ff5 to $7ffc do + write(char(buf[i])); + writeln; + + { find message base } + + base:=0; + while (buf[base]<>sig1) or (buf[base+1]<>sig2) do begin + inc(base,$1000); + if base=sizeof(buf) then begin + write('Signature not found !'); + halt(1); + end; + end; + + { calculate old checksum } + + oldsum:=0; + for i:=base+3 to base+255 do + inc(oldsum,buf[i]); + + { insert new text } + + move(newtext,buf[base+3],253); + inc(buf[base+2],oldsum-newsum); { update checksum } + + { write back to file } + + seek(fi,biosstart+base); iochk; + blockwrite(fi,buf[base],256); iochk; + close(fi); iochk; + writeln('BIOS file updated.'); +end. diff --git a/wrap/logo/PUTLOGO.TXT b/wrap/logo/PUTLOGO.TXT new file mode 100644 index 0000000..675d92b --- /dev/null +++ b/wrap/logo/PUTLOGO.TXT @@ -0,0 +1,5 @@ +WRAP BIOS custom startup message pd 040428 + +To insert a custom startup message, create a text file as needed. +(e.g. TEST.MSG). Then edit the batch file P.BAT to the right file +names. Update the BIOS with SETBIOS.COM or using the serial update. diff --git a/wrap/logo/TEST.MSG b/wrap/logo/TEST.MSG new file mode 100644 index 0000000..8745b82 --- /dev/null +++ b/wrap/logo/TEST.MSG @@ -0,0 +1,4 @@ +WRAP.1C v1.01 + +Your message here... + diff --git a/wrap/wrap1.bat b/wrap/wrap1.bat new file mode 100644 index 0000000..2072d94 --- /dev/null +++ b/wrap/wrap1.bat @@ -0,0 +1,4 @@ +a386 wrap_cfg.8 wrap_cfg.bin +a386 wrap.8 wrap.bin +..\tool\biossum wrap.bin wrap.abs +..\tool\cat pxe.bin ..\fill32.bin ..\fill32.bin wrap_cfg.bin wrap.abs >wrap1.rom