Gmail Calendar Documents Reader Web more
Recently Visited Groups | Help | Sign in
Google Groups Home
Message from discussion CPU Identification 1 of 4
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Michael A. Shiels  
View profile  
 More options Aug 16 1989, 3:04 am
Newsgroups: comp.sys.ibm.pc
Followup-To: comp.sys.ibm.pc
From: mshi...@tmsoft.uucp (Michael A. Shiels)
Date: 16 Aug 89 11:04:51 GMT
Local: Wed, Aug 16 1989 3:04 am
Subject: CPU Identification 1 of 4

    title    CPUID - Determine CPU & NDP Type
    page    58,122
    name    CPUID

;
;    CPUID uniquely identifies each NEC & Intel CPU & NDP.
;
; Notes on program structure:
;
;    This program uses four segments, two classes, and one group.
;    It demonstrates a useful technique for programmers who generate
;    .COM programs.  In particular, it shows how to use segment
;    classes to re-order segments, and how to eliminate the linker's
;    warning message about the absence of a stack segment.
;
;    The correspondence between segments and classes is as follows:
;
;            Segment        Class
;            -------        -----
;            STACK        prog
;            DATA        data
;            MDATA        data
;            CODE        prog
;
;    The segments apprear in the above order in the program source
;    to avoid forward references in the CODE segment to labels in
;    the DATA/MDATA segments.  However, because the STACK segment
;    appears first in the file, it and all segments in the same
;    class are made contiguous by the linker.  Thus they precede
;    the DATA/MDATA segments in the resulting .COM file because
;    the latter are in a different class.  In this manner, although
;    DATA and MDATA precede CODE in the source file, their order
;    is swapped in the .COM file.  That way there is no need for
;    an initial skip over the data areas to get to the CODE
;    segment.  As a side benefit, declaring a STACK segment (as
;    the first segment in the source) also eliminates the linker's
;    warning about that segment missing.  Finally, all segments
;    are declared to be in the same group so the linker can properly
;    resolve offsets.
;
;    Note that if you re-assemble the code for any reason, it is
;    important to use an assembler later than the IBM version 1.0.
;    That version has a number of bugs including an annoying habit
;    of alphabetizing segment names in the .OBJ file.  If you use
;    IBM MASM 2.0, be sure to specify /S to order the segments
;    properly.
;
;    If the program reports results at variance with your knowledge
;    of the system, please contact the author.
;
; Environments tested in:
;
;            CPU Speed
;    System         in MHz        CPU        NDP
;    ------        ---------    ---        ---
;    IBM PC AT    6        Intel 80286    Intel 80287
;    IBM PC AT    9        Intel 80286    Intel 80287
;    IBM PC AT    6        Intel 80286    none
;    IBM PC AT    8.5        Intel 80286    none
;    IBM PC        4.77        Intel 8088    Intel 8087-3
;    IBM PC        4.77        Intel 8088*    Intel 8087-3
;    IBM PC XT    4.77        Intel 8088    none
;    IBM PC XT    4.77        Intel 8088    Intel 8087-3
;    IBM PC Portable    4.77        NEC V20        none
;    COMPAQ        4.77        Intel 8088    none
;    COMPAQ        4.77        NEC V20        none
;    AT&T PC 6300    8        Intel 8086    Intel 8087-2
;    AT&T PC 6300    8        NEC V30        Intel 8087-2
;    Tandy 2000    8        Intel 80186    none
;
;    * = faulty CPU
;
; Program structure:
;
;    Group PGROUP:
;    Stack   segment STACK, byte-aligned, stack,  class 'prog'
;    Program segment CODE,  byte-aligned, public, class 'prog'
;    Data    segment DATA,  byte-aligned, public, class 'data'
;    Data    segment MDATA, byte-aligned, public, class 'data'
;
; Assembly requirements:
;
;    Use MASM 1.25 or later.
;    With IBM's MASM 2.0 only, use /S to avoid alphabetizing the
;        segment names.
;    Use /r option to generate real NDP code.
;
;    MASM CPUID/r;            to convert .ASM to .OBJ
;    LINK CPUID;            to convert .OBJ to .EXE
;    EXE2BIN CPUID CPUID.COM        to convert .EXE to .COM
;    ERASE CPUID.EXE            to avoid executing .EXE
;
;    Note that the linker doesn't warn about a missing stack segment.
;
; Author:
;
;    Original code by:    Bob Smith    May 1985
;                Qualitas, Inc.
;                8314 Thoreau Dr.
;                Bethesda, MD   20817
;
;    Arthur Zachai suggested the technique to distinguish within the
;    808x and 8018x families by exploiting the difference in the
;    length of their pre-fetch instruction queues.
;
;    Published in PC Tech Journal - April 1986 - Vol 4 No 4

    subttl    Structures, Records, Equates, & Macros
    page
ARG_STR    struc
    dw    ?            ; caller's bp
ARG_OFF    dw    ?            ; caller's offset
ARG_SEG    dw    ?            ;          segment
ARG_FLG    dw    ?            ;          flags
ARG_STR    ends

; Record to define bits in the CPU's & NDP's flags' registers

CPUFLAGS record RO:1,NT:1,IOPL:2,OF:1,DF:1,IF:1,TF:1,SF:1,ZF:1,R1:1,AF:1,R2:1,PF:1,R3:1,CF:1

NDPFLAGS record R4:3,IC:1,RC:2,PC:2,IEM:1,R5:1,PM:1,UM:1,OM:1,ZM:1,DM:1,IM:1

;    FLG_PIQL    Pre-fetch instruction queue length, 0 => 4-byte
;                                1 => 6-byte
;    FLG_08        Intel 808x
;    FLG_NEC        NEC V20 or V30
;    FLG_18        Intel 8018x
;    FLG_28        Intel 8028x
;    FLG_87        Intel 8087
;    FLG_287        Intel 80287
;
;    FLG_CERR    Faulty CPU
;    FLG_NERR    Faulty NDP switch setting

FLG    record    RSVD:9,FLG_NERR:1,FLG_CERR:1,FLG_NDP:2,FLG_CPU:3

; CPU-related flags

FLG_PIQL    equ    001b shl FLG_CPU
FLG_08        equ    000b shl FLG_CPU
FLG_NEC        equ    010b shl FLG_CPU
FLG_18        equ    100b shl FLG_CPU
FLG_28        equ    110b shl FLG_CPU

FLG_8088    equ    FLG_08
FLG_8086    equ    FLG_08 or FLG_PIQL
FLG_V20        equ    FLG_NEC
FLG_v30        equ    FLG_NEC or FLG_PIQL
FLG_80188    equ    FLG_18
FLG_80186    equ    FLG_18 or FLG_PIQL
FLG_80286    equ    FLG_28 or FLG_PIQL

; NDP-related flags

;            00b shl FLG_NDP        Not Present
FLG_87        equ    01b shl FLG_NDP
FLG_287    equ    10b shl FLG_NDP
BEL        equ    07h
LF        equ    0ah
CR        equ    0dh
EOS        equ    '$'

POPFF    macro
    local    L1,L2
    jmp    short L2        ; skip over IRET
L1:
    iret                ; pop the cs & ip pushed below along
                    ; with the flags, our original purpose
L2:
    push    cs            ; prepare for IRET by pushing cs
    call    L1            ; push ip, jump to IRET
    endm                ; POPFF macro

TAB    macro    TYP
    push    bx            ; save for a moment
    and    bx,mask FLG_&TYP    ; isolate flags
    mov    cl,FLG_&TYP        ; shift amount
    shr    bx,cl            ; shift to low-order
    shl    bx,1            ; times two to index table of words
    mov    dx,TYP&MSG_TAB[bx]    ; ds:dx => descriptive message
    pop    bx            ; restore
    mov    ah,09h            ; function code to display string
    int    21h            ; request dos service
    endm                ; TAB macro
    page
INT_VEC    segment at 0            ; start INT_VEC segment
        dd    ?        ; pointer to INT 00h
INT01_OFF    dw    ?        ; pointer to INT 01h
INT01_SEG    dw    ?
INT_VEC    ends                ; end INT_VEC segment

PGROUP    group    STACK,CODE,DATA,MDATA

; The following segment both positions class 'prog' segments lower in
; memory than others so the first byte of the resulting .COM file is
; in the CODE segment, as well as satisfies the LINKer's need to have
; a stack segment.

STACK    segment    byte stack 'prog'    ; start STACK segment
STACK    ends                ; end STACK segment

I11_REC    record I11_PRN:2,I11_RSV1:2,I11_COM:3,I11_RSV2:1,I11_DISK:2,I11_VID:2,I11_RSV3:2,I11_NDP:1,I11_IPL:1

DATA    segment    byte public 'data'    ; start DATA segment
    assume    ds:PGROUP

OLDINT01_VEC    label    dword        ; save area for original INT 01h handler
OLDINT01_OFF    dw    ?
OLDINT01_SEG    dw    ?

NDP_CW        label    word        ; save area for NDP control word
        db    ?
NDP_CW_HI    db    0        ; high byte of control word
NDP_ENV        dw    7 dup(?)    ; save area for NDP environment

DATA    ends
    subttl    Message Data Area
    page
MDATA    segment    byte public 'data'    ; start MDATA segment
    assume    ds:PGROUP

MSG_START    db    'CPUID -- Version 1.0'
        db    CR,LF,CR,LF,EOS
MSG_8088    db    'CPU is an Intel 8088.'
        db    CR,LF,EOS
MSG_8086    db    'CPU is an Intel 8086.'
        db    CR,LF,EOS
MSG_V20        db    'CPU is an NEC V20.'
        db    CR,LF,EOS
MSG_V30        db    'CPU is an NEC V30.'
        db    CR,LF,EOS
MSG_80188    db    'CPU is an Intel 80188.'
        db    CR,LF,EOS
MSG_80186    db    'CPU is an Intel 80186.'
        db    CR,LF,EOS
MSG_UNK        db    'CPU is a maverick -- 80288??.'
        db    CR,LF,EOS
MSG_80286    db    'CPU is an Intel 80286.'
        db    CR,LF,EOS

CPUMSG_TAB    label    word
    dw    PGROUP:MSG_8088        ; 000 = Intel 8088
    dw    PGROUP:MSG_8086        ; 001 = Intel 8086
    dw    PGROUP:MSG_V20        ; 010 = NEC V20
    dw    PGROUP:MSG_V30        ; 011 = NEC V30
    dw    PGROUP:MSG_80188    ; 100 = Intel 80188
    dw    PGROUP:MSG_80186    ; 101 = Intel 80186
    dw    PGROUP:MSG_UNK        ; 110 = ?
    dw    PGROUP:MSG_80286    ; 111 = Intel 80286

NDPMSG_TAB    label    word
    dw    PGROUP:MSG_NDPX        ; 00 = No NDP
    dw    PGROUP:MSG_8087        ; 01 = Intel 8087
    dw    PGROUP:MSG_80287    ; 10 = Intel 80287

MSG_NDPX    db    'NDP is not present.'
        db    CR,LF,EOS
MSG_8087    db    'NDP is an Intel 8087.'
        db    CR,LF,EOS
MSG_80287    db    'NDP is an Intel 80287.'
        db    CR,LF,EOS

CERRMSG_TAB    label    word
    dw    PGROUP:MSG_CPUOK    ; 0 = CPU healthy
    dw    PGROUP:MSG_CPUBAD    ; 1 = CPU faulty

MSG_CPUOK    db    'CPU appears to be healthy.'
        db    CR,LF,EOS
MSG_CPUBAD    label    byte
        db    BEL,'*** CPU incorrectly allows interrupts '
        db    'after a change to SS ***',CR,LF
        db    'It should be replaced with a more recent '
        db    'version as it could crash the',CR,LF
        db    'system at seemingly random times.',CR,LF,EOS

NERRMSG_TAB    label    word
    dw    PGROUP:MSG_NDPSWOK    ; 0 = NDP switch set correctly
    dw    PGROUP:MSG_NDPSWERR    ; 1 = NDP switch set incorrectly

MSG_NDPSWOK    db    EOS        ; no message
MSG_NDPSWERR    label    byte
        db    '*** Although there is an NDP installed '
        db    'on this sytem, the corresponding',CR,LF
        db    'system board switch is not properly set.  '
        db    'To correct this, flip switch 2 of',CR,LF
        db    'switch block 1 on the system board.',CR,LF,EOS

MDATA    ends                ; end MDATA segment
    subttl    Main Routine
    page
CODE    segment    byte public 'prog'    ; start CODE segment
    assume    cs:PGROUP,ds:PGROUP,es:PGROUP
    org    100h            ; skip over PSP

INITIAL    proc    near
    mov    dx,offset ds:MSG_START    ; starting message
    mov    ah,09h            ; function code to display string
    int    21h            ; request DOS service

    call    CPUID            ; check the CPU's identity

    TAB    CPU            ; display CPU results
    TAB    NDP            ; display NDP results
    TAB    CERR            ; display CPU ERR results
    TAB    NERR            ; display NDP ERR results

    ret                ; return to DOS
INITIAL    endp                ; end INITIAL procedure
    subttl    CPUID Procedure
    page
CPUID    proc    near            ; start CPUID procedure
    assume    cs:PGROUP,ds:PGROUP,es:PGROUP

; This procedure determines the type of CPU and NDP (if any) in use.
;
; The possibilities include:
;
;        Intel 8086
;        Intel 8088
;        NEC V20
;        NEC V30
;        Intel 80186
;        Intel 80188
;        Intel 80286
;        Intel 8087
;        Intel 80287
;
; Also checked is whether or not the CPU allows interrupts after
; changing the SS register segment.  If the CPU does, it is faulty
; and should be replaced.
;
; Further, if an NDP is installed, non-AT machines should have a
; system board switch set.  Such a discrepancy is reported.
;
; On exit, BX contains flag settings (as defined in FLG record) which
; the caller can check.  For example, to test for an Intel 80286, use
;
;        and    bx,mask FLAG_CPU
;        cmp    bx,FLG_80286
;        je    ITSA286

    irp    XX,<ax,cx,di,ds,es>    ; save registers
    push    XX
    endm

; test for 80286 -- this CPU executes PUSH SP by first storing SP on
; stack, then decrementing it.  earlier CPU's decrement, THEN store.

    mov    bx,FLG_28        ; assume it's a 286
    push    sp            ; only 286 pushes pre-push SP
    pop    ax            ; get it back
    cmp    ax,sp            ; check for same
    je    CHECK_PIQL        ; they are, so it's a 286

; test for 80186/80188 -- 18xx and 286 CPU's mask shift/rotate
; operations mod 32; earlier CPUs use all 8 bits of CL.

    mov    bx,FLG_18        ; assume it's an 8018x
    mov    cl,32+1            ; 18x masks shift counts mod 32
                    ; note we can't use just 32 in CL
    mov    al,0ffh            ; start with all bits set

    shl    al,cl            ; shift one position if 18x
    jnz    CHECK_PIQL        ; some bits still on,
                    ; so its a 18x, check PIQL

; test for V20

    mov    bx,FLG_NEC        ; assume it's an NEC V-series CPU
    call    CHECK_NEC        ; see if it's an NEC chip
    jcxz    CHECK_PIQL        ; good guess, check PIQL

    mov    bx,FLG_08        ; it's an 808x
    subttl    Check Length of Pre-Fetch Instruction Queue
    page
; Check the length of the pre-fetch instruction queue (PIQ).
;
; xxxx6 CPUs have a PIQ length of 6 bytes,
; xxxx8 CPUs have a PIQ length of 4 bytes
;
; Self-modifying code is used to distinguish the two PIQ lengths.

CHECK_PIQL:
    call    PIQL_SUB        ; handle via subroutine
    jcxz    CHECK_ERR        ; if CX is 0, INC was not executed,
                    ; hence PIQ length is 4
    or    bx,FLG_PIQL        ; PIQ length is 6
    subttl    Check for Allowing Interrupts After POP SS
    page
; Test for faulty chip (allows interrupts after change to SS register)

CHECK_ERR:
    xor    ax,ax            ; prepare to address
                    ; interrupt vector segment
    mov    ds,ax            ; DS points to segment 0
    assume    ds:INT_VEC        ; tell the assembler

    cli                ; nobody move while we swap

    mov    ax,offset cs:INT01    ; point to our own handler
    xchg    ax,INT01_OFF        ; get and swap offset
    mov    OLDINT01_OFF,ax        ; save to restore later

    mov    ax,cs            ; our handler's segment
    xchg    ax,INT01_SEG        ; get and swap segment
    mov    OLDINT01_SEG,ax        ; save to restore later

; note we continue with interrupts disabled to avoid
; an external interrupt occuring during this test

    mov    cx,1            ; initialize a register
    push    ss            ; save ss to store back into itself
    pushf                ; move flags
    pop    ax            ; ... into ax
    or    ax,mask TF        ; set trap flag
    push    ax            ; place onto stack
    POPFF                ; ... and then into effect
                    ; some CPUs effect the trap flag
                    ; immediately, some
                    ; wait one instruction
    nop                ; allow interrupt to take effect

POST_NOP:
    pop    ss            ; change the stack segment register
                    ; (to itself)
    dec    cx            ; normal cpu's execute this instruction
                    ; before recognizing the single-step
                    ; interrupt
    hlt                ; we never get here

INT01:

; Note: IF=TF=0
; If we're stopped at or before POST_NOP, continue on

    push    bp            ; prepare to address the stack
    mov    bp,sp            ; hello, Mr. stack

    cmp    [bp].ARG_OFF,offset cs:POST_NOP    ; check offset
    pop    bp            ; restore
    ja    INTO1_DONE        ; we're done

    iret                ; return to caller

INTO1_DONE:

; restore old INT 01h handler

    les    ax,OLDINT01_VEC    ; ES:AX ==> old INT 01h handler
    assume    es:nothing        ; tell the assembler
    mov    INT01_OFF,ax        ; restore offset
    mov    INT01_SEG,es        ; ... and segment
    sti                ; allow interrupts again (IF=1)

    add    sp,3*2            ; strip ip, cs, and flags from stack

    push    cs            ; setup ds for code below
    pop    ds
    assume    ds:PGROUP        ; tell the assembler

    jcxz    CHECK_NDP        ; if cx is 0, the dec cx was executed,
                    ; and the cpu is ok
    or    bx,mask FLG_CERR    ; it's a faulty chip
    subttl    Check For Numeric Data Processor
    page
; Test for a Numeric Data Processor -- Intel 8087 or 80287.  The
; technique used is passive -- it leaves the NDP in the same state in
; which it is found.

CHECK_NDP:
    cli                ; protect FNSTENV
    fnstenv NDP_ENV            ; if NDP present, save
                    ; current environment,
                    ; otherwise, this instruction
                    ; is ignored
    mov    cx,50/7            ; cycle this many times
    loop    $            ; wait for result to be stored
    sti                ; allow interrupts
    fninit                ; initialize processor to known state
    jmp    short $+2        ; wait for initialization

    fnstcw    NDP_CW            ; save control word
    jmp    short $+2        ; wait for result to be stored
    jmp    short $+2
    cmp    NDP_CW_HI,03h        ; check for NDP initial control word
    jne    CPUID_EXIT        ; no NDP installed
    int    11h            ; get equipment flags into ax
    test    ax,mask I11_NDP        ; check NDP-installed bit
    jnz    CHECK_NDP1        ; it's correctly set
    or    bx,mask FLG_NERR    ; mark as in error
CHECK_NDP1:
    and    NDP_CW,not mask IEM    ; enable interrupts
                    ; (IEM=0, 8087 only)
    fldcw    NDP_CW            ; reload control word
    fdisi                ; disable interrupts (IEM=1) on 8087,
                    ; ignored by 80287
    fstcw    NDP_CW            ; save control word
    fldenv    NDP_ENV            ; restore original NDP environment
                    ; no need to wait
                    ; for environment to be loaded
    test    NDP_CW,mask IEM        ; check interrupt enable mask
                    ; (8087 only)
    jnz    CPUID_8087        ; it changed, hence NDP is an 8087
    or    bx,FLG_287        ; NDP is an 80287
    jmp    short CPUID_EXIT    ; exit with falgs in BX
CPUID_8087:
    or    bx,FLG_87        ; NDP is an 8087
CPUID_EXIT:
    irp    XX,<es,ds,di,cx,ax>    ; restore registers
    pop    XX
    endm
    assume    ds:nothing,es:nothing
    ret                ; return to caller
CPUID    endp                ; end CPUID procedure
    subttl    Check For NEC V20/V30
    page
CHECK_NEC    proc    near

; The NEC V20/V30 are very compatible with the Intel 8086/8088.
; The only point of "incompatibility" is that they do not contain
; a bug found in the Intel CPU's.  Specifically, the NEC CPU's
; correctly restart an interrupted multi-prefix string instruction
; at the start of the instruction.  The Intel CPU's incorrectly
; restart in the middle of the instruction.  This routine tests
; for that situation by executing such an instruction for a
; sufficiently long period of time for a timer interrupt to occur.
; If at the end of the instruction, CX is zero, it must be an NEC
; CPU; if not, it's an Intel CPU.
;
; Note that we're counting on the timer interrupt to do its thing
; every 18.2 times per second.
;
; Here's a worst case analysis: An Intel 8088/8086 executes 65535
; iterations of LODSB ES[SI] in 2+9+13*65535 = 851,966 clock ticks.
; If the Intel 8088/8086 is running at 10 MHz, each clock tick is
; 100 nanoseconds, hence the entire operation takes 85 milliseconds.
; If the timer is running at normal speed, it interrupts the CPU every
; 55ms and so should interrupt the repeated string instruction at least
; once.

    mov    cx,0ffffh        ; move a lot of data
    sti                ; ensure timer enabled

; execute multi-prefix instruction.  note that the value of ES as
; well as the direction flag setting is irrelevant.

    push    ax            ; save registers
    push    si
    rep    lods    byte ptr es:[si]
    pop    si            ; restore
    pop    ax

; on exit: if cx is zero, it's an NEC CPU, otherwise it's an Intel CPU

    ret                ; return to caller
CHECK_NEC    endp
    subttl    Pre-Fetch Instruction Queue Subroutine
    page
PIQL_SUB    proc    near

; This subroutine discerns the length of the CPU's pre-fetch
; instruction queue (PIQ).
;
; The technique used is to first ensure that the PIQ is full, then
; change an instruction which should be in a 6-byte PIQ but not in a
; 4-byte PIQ.  Then, if the original instruction is executed, the PIQ
; is 6-bytes long; if the new instruction is executed, PIQ length is 4.
;
; We ensure the PIQ is full be executing an instruction which takes
; long enough so that the Bus Interface Unit (BIU) can fill the PIQ
; while the instruction is executing.
;
; Specifically, for all byt the last STOSB, we're simple marking time
; waiting for the BIU to fill the PIQ.  The last STOSB actually changes
; the instruction.  By that time, the orignial instruction should be in
; a six-byte PIQ byt not a four-byte PIQ.

    assume    cs:PGROUP,es:PGROUP
@REP    equ    3            ; repeat the store this many times
    std                ; store backwards
    mov    di,offset es:LAB_INC+@REP-1    ; change the instructions
                    ; at ES:DI
                    ; and preceding
    mov    al,ds:LAB_STI        ; change to a sti
    mov    cx,@REP            ; give the BIU time
                    ; to pre-fetch instructions
    cli                ; ensure interrupts are disabled,
                    ; otherwise a timer tick
                    ; could change the PIQ filling
    rep    stosb            ; change the instruction
                    ; during execution of this instruction
                    ; the BIU is refilling the PIQ.  The
                    ; current instruction is no longer
                    ; in the PIQ.
                    ; Note at end, CX is 0.

; The PIQ begins filling here

    cld                ; restore direction flag
    nop                ; PIQ fillers
    nop
    nop

; The following instruction is beyond a four-byte-PIQ CPU's reach,
; but within that of a six-byte-PIQ CPU.

LAB_INC        label    byte
    inc    cx            ; executed only if PIQ length is 6

LAB_STI    label     byte
    rept    @REP-1
    sti                ; restore interrupts
    endm
    ret                ; return to caller
    assume    ds:nothing,es:nothing
PIQL_SUB    endp            ; end PIQL_SUB procedure

CODE    ends                ; end code segment

    if1
%OUT    Pass 1 Complete
    else
%OUT    Pass 2 Complete
    endif

    end    INITIAL            ; end CPUID module


    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2010 Google