Subversion Repositories Kolibri OS

Rev

Rev 1703 | Rev 1952 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
  4. ;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa      ;;
  5. ;; Distributed under terms of the GNU General Public License    ;;
  6. ;;                                                              ;;
  7. ;;  BOOTCODE.INC                                                ;;
  8. ;;                                                              ;;
  9. ;;  KolibriOS 16-bit loader,                                    ;;
  10. ;;                        based on bootcode for MenuetOS        ;;
  11. ;;                                                              ;;
  12. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  13.  
  14. $Revision: 1941 $
  15.  
  16.  
  17. ;==========================================================================
  18. ;
  19. ;                           16 BIT FUNCTIONS
  20. ;
  21. ;==========================================================================
  22.  
  23. if 0
  24. putchar:
  25. ; in: al=character
  26.         mov     ah, 0Eh
  27.         mov     bh, 0
  28.         int     10h
  29.         ret
  30.  
  31. print:
  32. ; in: si->string
  33.         mov     al, 186
  34.         call    putchar
  35.         mov     al, ' '
  36.         call    putchar
  37.  
  38. printplain:
  39. ; in: si->string
  40.         pusha
  41.         lodsb
  42. @@:
  43.         call    putchar
  44.         lodsb
  45.         test    al,al
  46.         jnz     @b
  47.         popa
  48.         ret
  49.  
  50.  
  51. setcursor:
  52. ; in: dl=column, dh=row
  53.         mov     ah, 2
  54.         mov     bh, 0
  55.         int     10h
  56.         ret
  57.  
  58. macro _setcursor row,column
  59. {
  60.         mov     dx, row*256 + column
  61.         call    setcursor
  62. }
  63.  
  64. end if
  65.  
  66. boot_read_floppy:
  67.         push    si
  68.         xor     si, si
  69.         mov     ah, 2   ; read
  70. @@:
  71.         push    ax
  72.         int     0x13
  73.         pop     ax
  74.         jnc     @f
  75.         inc     si
  76.         cmp     si, 10
  77.         jnb     $
  78. @@:
  79.         pop     si
  80.         ret
  81.  
  82. sayerr_plain:
  83.         jmp     $
  84.  
  85. sayerr:
  86.         jmp     $
  87.  
  88.  
  89. ; convert abs. sector number (AX) to BIOS T:H:S
  90. ; sector number = (abs.sector%BPB_SecPerTrk)+1
  91. ; pre.track number = (abs.sector/BPB_SecPerTrk)
  92. ; head number = pre.track number%BPB_NumHeads
  93. ; track number = pre.track number/BPB_NumHeads
  94. ; Return: cl - sector number
  95. ;         ch - track number
  96. ;         dl - drive number (0 = a:)
  97. ;         dh - head number
  98. conv_abs_to_THS:
  99.         push    bx
  100.         mov     bx,word [BPB_SecPerTrk]
  101.         xor     dx,dx
  102.         div     bx
  103.         inc     dx
  104.         mov     cl, dl                          ; cl = sector number
  105.         mov     bx,word [BPB_NumHeads]
  106.         xor     dx,dx
  107.         div     bx
  108.         ; !!!!!!! ax = track number, dx = head number
  109.         mov     ch,al                           ; ch=track number
  110.         xchg    dh,dl                           ; dh=head number
  111.         mov     dl,0                            ; dl=0 (drive 0 (a:))
  112.         pop     bx
  113.         retn
  114. ; needed variables
  115. BPB_SecPerTrk   dw      0                       ; sectors per track
  116. BPB_NumHeads    dw      0                       ; number of heads
  117. BPB_FATSz16     dw      0                       ; size of FAT
  118. BPB_RootEntCnt  dw      0                       ; count of root dir. entries
  119. BPB_BytsPerSec  dw      0                       ; bytes per sector
  120. BPB_RsvdSecCnt  dw      0                       ; number of reserved sectors
  121. BPB_TotSec16    dw      0                       ; count of the sectors on the volume
  122. BPB_SecPerClus  db      0                       ; number of sectors per cluster
  123. BPB_NumFATs     db      0                       ; number of FAT tables
  124. abs_sector_adj  dw      0                       ; adjustment to make abs. sector number
  125. end_of_FAT      dw      0                       ; end of FAT table
  126. FirstDataSector dw      0                       ; begin of data
  127.  
  128. ;=========================================================================
  129. ;
  130. ;                           16 BIT CODE
  131. ;
  132. ;=========================================================================
  133.  
  134. include 'bootvesa.inc'                 ;Include source for boot vesa
  135.  
  136. start_of_code:
  137.         cld
  138. ; \begin{diamond}[02.12.2005]
  139. ; if bootloader sets ax = 'KL', then ds:si points to loader block
  140.         cmp     ax, 'KL'
  141.         jnz     @f
  142.         mov     word [cs:cfgmanager.loader_block], si
  143.         mov     word [cs:cfgmanager.loader_block+2], ds
  144. @@:
  145. ; \end{diamond}[02.12.2005]
  146.  
  147. ; if bootloader sets cx = 'HA' and dx = 'RD', then bx contains identifier of source hard disk
  148. ; (see comment to bx_from_load)
  149.         cmp     cx, 'HA'
  150.         jnz     no_hd_load
  151.         cmp     dx,'RD'
  152.         jnz     no_hd_load
  153.         mov     word [cs:bx_from_load], bx              ; {SPraid}[13.03.2007]
  154. no_hd_load:
  155.  
  156. ; set up stack
  157.         mov     ax, 3000h
  158.         mov     ss, ax
  159.         mov     sp, 0EC00h
  160. ; set up segment registers
  161.         push    cs
  162.         pop     ds
  163.         push    cs
  164.         pop     es
  165.  
  166. ; set videomode
  167.         mov     ax, 3
  168.         int     0x10
  169.  
  170. ; draw frames
  171.         push    0xb800
  172.         pop     es
  173.         xor     di, di
  174.         mov     ah, 1*16+15
  175.  
  176. cpugood:
  177.  
  178.         push    0
  179.         popf
  180.         sti
  181.  
  182. ; set up esp
  183.         movzx   esp, sp
  184.  
  185.         push    0
  186.         pop     es
  187.         and     word [es:0x9031], 0
  188. ; \begin{Mario79}
  189. ; find HDD IDE DMA PCI device
  190. ; check for PCI BIOS
  191.         mov     ax, 0xB101
  192.         int     0x1A
  193.         jc      .nopci
  194.         cmp     edx, 'PCI '
  195.         jnz     .nopci
  196. ; find PCI class code
  197. ; class 1 = mass storage
  198. ; subclass 1 = IDE controller
  199. ; a) class 1, subclass 1, programming interface 0x80
  200.         mov     ax, 0xB103
  201.         mov     ecx, 1*10000h + 1*100h + 0x80
  202.         xor     si, si  ; device index = 0
  203.         int     0x1A
  204.         jnc     .found
  205. ; b) class 1, subclass 1, programming interface 0x8A
  206.         mov     ax, 0xB103
  207.         mov     ecx, 1*10000h + 1*100h + 0x8A
  208.         xor     si, si  ; device index = 0
  209.         int     0x1A
  210.         jnc     .found
  211. ; c) class 1, subclass 1, programming interface 0x85
  212.         mov     ax, 0xB103
  213.         mov     ecx, 1*10000h + 1*100h + 0x85
  214.         xor     si, si
  215.         int     0x1A
  216.         jc      .nopci
  217. .found:
  218. ; get memory base
  219.         mov     ax, 0xB10A
  220.         mov     di, 0x20        ; memory base is config register at 0x20
  221.         int     0x1A
  222.         jc      .nopci
  223.         and     cx, 0xFFF0      ; clear address decode type
  224.         mov     [es:0x9031], cx
  225. .nopci:
  226. ; \end{Mario79}
  227.  
  228. ; --------------- APM ---------------------
  229.         and     word [es:0x9044], 0     ; ver = 0.0 (APM not found)
  230.         mov     ax, 0x5300
  231.         xor     bx, bx
  232.         int     0x15
  233.         jc      apm_end                 ; APM not found
  234.         test    cx, 2
  235.         jz      apm_end                 ; APM 32-bit protected-mode interface not supported
  236.         mov     [es:0x9044], ax         ; Save APM Version
  237.         mov     [es:0x9046], cx         ; Save APM flags
  238.  
  239.         mov     ax, 0x5304              ; Disconnect interface
  240.         xor     bx, bx
  241.         int     0x15
  242.         mov     ax, 0x5303              ; Connect 32 bit mode interface
  243.         xor     bx, bx
  244.         int     0x15
  245.  
  246.         mov     [es:0x9040], ebx
  247.         mov     [es:0x9050], ax
  248.         mov     [es:0x9052], cx
  249.         mov     [es:0x9054], dx
  250.  
  251. apm_end:
  252.  
  253. ;CHECK current of code
  254.         cmp     [cfgmanager.loader_block], -1
  255.         jz      noloaderblock
  256.         les     bx, [cfgmanager.loader_block]
  257.         cmp     byte [es:bx], 1
  258.         jnz     sayerr
  259.         push    0
  260.         pop     es
  261.  
  262. noloaderblock:
  263. ; DISPLAY VESA INFORMATION
  264.          call    print_vesa_info
  265.          call    calc_vmodes_table
  266.          call    check_first_parm  ;check and enable cursor_pos
  267.  
  268. ; \begin{diamond}[30.11.2005]
  269. cfgmanager:
  270. ; settings:
  271. ; a) preboot_graph = graphical mode
  272. ;    preboot_gprobe = probe this mode?
  273. ; b) preboot_dma  = use DMA access?
  274. ; c) preboot_vrrm = use VRR?
  275. ; d) preboot_device = from what boot?
  276.  
  277. ; determine default settings
  278. ;        mov     [.bSettingsChanged], 0
  279.  
  280. ;.preboot_gr_end:
  281.         mov     di, preboot_device
  282. ; if image in memory is present and [preboot_device] is uninitialized,
  283. ; set it to use this preloaded image
  284.         cmp     byte [di], 0
  285.         jnz     .preboot_device_inited
  286.         cmp     [.loader_block], -1
  287.         jz      @f
  288.         les     bx, [.loader_block]
  289.         test    byte [es:bx+1], 1
  290.         jz      @f
  291.         mov     byte [di], 3
  292.         jmp     .preboot_device_inited
  293. @@:
  294. ; otherwise, set [preboot_device] to 1 (default value - boot from floppy)
  295.         mov     byte [di], 1
  296. .preboot_device_inited:
  297. ; following 4 lines set variables to 1 if its current value is 0
  298.         cmp     byte [di+preboot_dma-preboot_device], 1
  299.         adc     byte [di+preboot_dma-preboot_device], 0
  300.         cmp     byte [di+preboot_biosdisk-preboot_device], 1
  301.         adc     byte [di+preboot_biosdisk-preboot_device], 0
  302.  
  303. ;        pop     ax              ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ??
  304.         jmp     .continue
  305. .loader_block dd -1
  306. .continue:
  307.         sti
  308.         jmp      .load
  309.  
  310. .loadc:
  311.         pop     eax
  312. .cont:
  313.         push    cs
  314.         pop     ds
  315. .load:
  316.  
  317. ; ASK GRAPHICS MODE
  318.  
  319.         call    set_vmode
  320.  
  321. ; GRAPHICS ACCELERATION
  322. ; force yes
  323.         mov     [es:0x901C], byte 1
  324.  
  325. ; DMA ACCESS TO HD
  326.  
  327.         mov     al, [preboot_dma]
  328.         mov     [es:0x901F], al
  329.  
  330. ; VRR_M USE
  331.  
  332.         mov     al,[preboot_vrrm]
  333.         mov     [es:0x9030], al
  334.         mov     [es:0x901E], byte 1
  335.  
  336. ; BOOT DEVICE
  337.  
  338.         mov     al, [preboot_device]
  339.         dec     al
  340.         mov     [boot_dev], al
  341.  
  342. ; GET MEMORY MAP
  343. include 'detect/biosmem.inc'
  344.  
  345. ; READ DISKETTE TO MEMORY
  346.  
  347.         cmp     [boot_dev],0
  348.         jne     no_sys_on_floppy
  349.         xor     ax, ax            ; reset drive
  350.         xor     dx, dx
  351.         int     0x13
  352. ; do we boot from CD-ROM?
  353.         mov     ah, 41h
  354.         mov     bx, 55AAh
  355.         xor     dx, dx
  356.         int     0x13
  357.         jc      .nocd
  358.         cmp     bx, 0AA55h
  359.         jnz     .nocd
  360.         mov     ah, 48h
  361.         push    ds
  362.         push    es
  363.         pop     ds
  364.         mov     si, 0xa000
  365.         mov     word [si], 30
  366.         int     0x13
  367.         pop     ds
  368.         jc      .nocd
  369.         push    ds
  370.         lds     si, [es:si+26]
  371.         test    byte [ds:si+10], 40h
  372.         pop     ds
  373.         jz      .nocd
  374. ; yes - read all floppy by 18 sectors
  375.  
  376. ; TODO: !!!! read only first sector and set variables !!!!!
  377. ; ...
  378. ; TODO: !!! then read flippy image track by track
  379.  
  380.         mov     cx, 0x0001      ; startcyl,startsector
  381. .a1:
  382.         push    cx dx
  383.         mov     al, 18
  384.         mov     bx, 0xa000
  385.         call    boot_read_floppy
  386.         mov     si, movedesc
  387.         push    es
  388.         push    ds
  389.         pop     es
  390.         mov     cx, 256*18
  391.         mov     ah, 0x87
  392.         int     0x15
  393.         pop     es
  394.         pop     dx cx
  395.         test    ah, ah
  396.         jnz     sayerr_floppy
  397.         add     dword [si+8*3+2], 512*18
  398.         inc     dh
  399.         cmp     dh, 2
  400.         jnz     .a1
  401.         mov     dh, 0
  402.         inc     ch
  403.         cmp     ch, 80
  404.         jae     ok_sys_on_floppy
  405.         mov     al, ch
  406.         shr     ch, 2
  407.         jmp     .a1
  408. .nocd:
  409. ; no - read only used sectors from floppy
  410. ; now load floppy image to memory
  411. ; at first load boot sector and first FAT table
  412.  
  413. ; read only first sector and fill variables
  414.         mov     cx, 0x0001      ; first logical sector
  415.         xor     dx, dx          ; head = 0, drive = 0 (a:)
  416.         mov     al, 1           ; read one sector
  417.         mov     bx, 0xB000      ; es:bx -> data area
  418.         call    boot_read_floppy
  419. ; fill the necessary parameters to work with a floppy
  420.         mov     ax, word [es:bx+24]
  421.         mov     word [BPB_SecPerTrk], ax
  422.         mov     ax, word [es:bx+26]
  423.         mov     word [BPB_NumHeads], ax
  424.         mov     ax, word [es:bx+17]
  425.         mov     word [BPB_RootEntCnt], ax
  426.         mov     ax, word [es:bx+14]
  427.         mov     word [BPB_RsvdSecCnt], ax
  428.         mov     ax, word [es:bx+19]
  429.         mov     word [BPB_TotSec16], ax
  430.         mov     al, byte [es:bx+13]
  431.         mov     byte [BPB_SecPerClus], al
  432.         mov     al, byte [es:bx+16]
  433.         mov     byte [BPB_NumFATs], al
  434. ;<Lrz> 18.11.2008
  435.         mov     ax, word [es:bx+22]
  436.         mov     word [BPB_FATSz16], ax
  437.         mov     cx, word [es:bx+11]
  438.         mov     word [BPB_BytsPerSec], cx
  439.  
  440. ; count of clusters in FAT12 ((size_of_FAT*2)/3)
  441. ;        mov     ax, word [BPB_FATSz16]
  442. ;        mov     cx, word [BPB_BytsPerSec]
  443. ;end <Lrz> 18.11.2008
  444.         xor     dx, dx
  445.         mul     cx
  446.         shl     ax, 1
  447.         mov     cx, 3
  448.         div     cx              ; now ax - number of clusters in FAT12
  449.         mov     word [end_of_FAT], ax
  450.  
  451. ; load first FAT table
  452.         mov     cx, 0x0002      ; startcyl,startsector          ; TODO!!!!!
  453.         xor     dx, dx          ; starthead,drive
  454.         mov     al, byte [BPB_FATSz16]     ; no of sectors to read
  455.         add     bx, word [BPB_BytsPerSec]  ; es:bx -> data area
  456.         call    boot_read_floppy
  457.         mov     bx, 0xB000
  458.  
  459. ; and copy them to extended memory
  460.         mov     si, movedesc
  461.         mov     [si+8*2+3], bh          ; from
  462.  
  463.         mov     ax, word [BPB_BytsPerSec]
  464.         shr     ax, 1                   ; words per sector
  465.         mov     cx, word [BPB_RsvdSecCnt]
  466.         add     cx, word [BPB_FATSz16]
  467.         mul     cx
  468.         push    ax                      ; save to stack count of words in boot+FAT
  469.         xchg    ax, cx
  470.  
  471.         push    es
  472.         push    ds
  473.         pop     es
  474.         mov     ah, 0x87
  475.         int     0x15
  476.         pop     es
  477.         test    ah, ah
  478.         jz      @f
  479. sayerr_floppy:
  480.         mov     dx, 0x3f2
  481.         mov     al, 0
  482.         out     dx, al
  483. ;        mov     si, memmovefailed
  484.         jmp     $
  485. @@:
  486.         pop     ax                      ; restore from stack count of words in boot+FAT
  487.         shl     ax, 1                   ; make bytes count from count of words
  488.         and     eax, 0ffffh
  489.         add     dword [si+8*3+2], eax
  490.  
  491. ; copy first FAT to second copy
  492. ; TODO: BPB_NumFATs !!!!!
  493.         add     bx, word [BPB_BytsPerSec]       ; !!! TODO: may be need multiply by BPB_RsvdSecCnt !!!
  494.         mov     byte [si+8*2+3], bh     ; bx - begin of FAT
  495.  
  496.         mov     ax, word [BPB_BytsPerSec]
  497.         shr     ax, 1                   ; words per sector
  498.         mov     cx, word [BPB_FATSz16]
  499.         mul     cx
  500.         mov     cx, ax                  ; cx - count of words in FAT
  501.  
  502.         push    es
  503.         push    ds
  504.         pop     es
  505.         mov     ah, 0x87
  506.         int     0x15
  507.         pop     es
  508.         test    ah, ah
  509.         jnz     sayerr_floppy
  510.  
  511.         mov     ax, cx
  512.         shl     ax, 1
  513.         and     eax, 0ffffh             ; ax - count of bytes in FAT
  514.         add     dword [si+8*3+2], eax
  515.  
  516. ; reading RootDir
  517. ; TODO: BPB_NumFATs
  518.         add     bx, ax
  519.         add     bx, 100h
  520.         and     bx, 0ff00h                      ; bx - place in buffer to write RootDir
  521.         push    bx
  522.  
  523.         mov     bx, word [BPB_BytsPerSec]
  524.         shr     bx, 5                           ; divide bx by 32
  525.         mov     ax, word [BPB_RootEntCnt]
  526.         xor     dx, dx
  527.         div     bx
  528.         push    ax                              ; ax - count of RootDir sectors
  529.  
  530.         mov     ax, word [BPB_FATSz16]
  531.         xor     cx, cx
  532.         mov     cl, byte [BPB_NumFATs]
  533.         mul     cx
  534.         add     ax, word [BPB_RsvdSecCnt]       ; ax - first sector of RootDir
  535.  
  536.         mov     word [FirstDataSector], ax
  537.         pop     bx
  538.         push    bx
  539.         add     word [FirstDataSector], bx      ; Begin of data region of floppy
  540.  
  541. ; read RootDir
  542.         call    conv_abs_to_THS
  543.         pop     ax
  544.         pop     bx                              ; place in buffer to write
  545.         push    ax
  546.         call    boot_read_floppy                ; read RootDir into buffer
  547. ; copy RootDir
  548.         mov     byte [si+8*2+3], bh             ; from buffer
  549.         pop     ax                              ; ax = count of RootDir sectors
  550.         mov     cx, word [BPB_BytsPerSec]
  551.         mul     cx
  552.         shr     ax, 1
  553.         mov     cx, ax                          ; count of words to copy
  554.         push    es
  555.         push    ds
  556.         pop     es
  557.         mov     ah, 0x87
  558.         int     0x15
  559.         pop     es
  560.  
  561.         mov     ax, cx
  562.         shl     ax, 1
  563.         and     eax, 0ffffh             ; ax - count of bytes in RootDir
  564.         add     dword [si+8*3+2], eax   ; add count of bytes copied
  565.  
  566. ; Reading data clusters from floppy
  567.         mov     byte [si+8*2+3], bh
  568.         push    bx
  569.  
  570.         mov     di, 2                   ; First data cluster
  571. .read_loop:
  572.         mov     bx, di
  573.         shr     bx, 1                   ; bx+di = di*1.5
  574.         jnc     .even
  575.         test    word [es:bx+di+0xB200], 0xFFF0  ; TODO: may not be 0xB200 !!!
  576.         jmp     @f
  577. .even:
  578.         test    word [es:bx+di+0xB200], 0xFFF   ; TODO: may not be 0xB200 !!!
  579.  
  580. @@:
  581.         jz      .skip
  582. ; read cluster di
  583. ;.read:
  584.         ;conv cluster di to abs. sector ax
  585.         ; ax = (N-2) * BPB_SecPerClus + FirstDataSector
  586.         mov     ax, di
  587.         sub     ax, 2
  588.         xor     bx, bx
  589.         mov     bl, byte [BPB_SecPerClus]
  590.         mul     bx
  591.         add     ax, word [FirstDataSector]
  592.         call    conv_abs_to_THS
  593.         pop     bx
  594.         push    bx
  595.         mov     al, byte [BPB_SecPerClus]       ; number of sectors in cluster
  596.         call    boot_read_floppy
  597.         push    es
  598.         push    ds
  599.         pop     es
  600.         pusha
  601. ;
  602.         mov     ax, word [BPB_BytsPerSec]
  603.         xor     cx, cx
  604.         mov     cl, byte [BPB_SecPerClus]
  605.         mul     cx
  606.         shr     ax, 1                           ; ax = (BPB_BytsPerSec * BPB_SecPerClus)/2
  607.         mov     cx, ax                          ; number of words to copy (count words in cluster)
  608. ;
  609.         mov     ah, 0x87
  610.         int     0x15                            ; copy data
  611.         test    ah, ah
  612.         popa
  613.         pop     es
  614.         jnz     sayerr_floppy
  615. ; skip cluster di
  616. .skip:
  617.         mov     ax, word [BPB_BytsPerSec]
  618.         xor     cx, cx
  619.         mov     cl, byte [BPB_SecPerClus]
  620.         mul     cx
  621.         and     eax, 0ffffh             ; ax - count of bytes in cluster
  622.         add     dword [si+8*3+2], eax
  623.  
  624.         mov     ax, word [end_of_FAT]   ; max cluster number
  625.         pusha
  626. ; draw percentage
  627. ; total clusters: ax
  628. ; read clusters: di
  629.         xchg    ax, di
  630.         mov     cx, 100
  631.         mul     cx
  632.         div     di
  633.         aam
  634.         xchg    al, ah
  635.         add     ax, '00'
  636. @@:
  637.         popa
  638.         inc     di
  639.         cmp     di, word [end_of_FAT]   ; max number of cluster
  640.         jnz     .read_loop
  641.         pop     bx                      ; clear stack
  642.  
  643. ok_sys_on_floppy:
  644. no_sys_on_floppy:
  645.         xor     ax, ax          ; reset drive
  646.         xor     dx, dx
  647.         int     0x13
  648.         mov     dx, 0x3f2       ; floppy motor off
  649.         mov     al, 0
  650.         out     dx, al
  651.  
  652.  
  653. ; SET GRAPHICS
  654.  
  655.         xor     ax, ax
  656.         mov     es, ax
  657.  
  658.         mov     bx, [es:0x9008]         ; vga & 320x200
  659.         mov     ax, 0x4f02              ; Vesa
  660. setgr:
  661.         int     0x10
  662.         test    ah, ah
  663.         jnz     $
  664. gmok2:
  665.         push    ds
  666.         pop     es
  667.