Subversion Repositories Kolibri OS

Rev

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

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8. $Revision: 3762 $
  9.  
  10.  
  11. ; Low-level driver for HDD access
  12. ; DMA support by Mario79
  13. ; Access through BIOS by diamond
  14. ; LBA48 support by Mario79
  15. ;-----------------------------------------------------------------------------
  16. struct HD_DATA
  17. hdbase  dd      ?
  18. hdid    dd      ?
  19. hdpos   dd      ?
  20. ends
  21.  
  22. iglobal
  23. align 4
  24. ide_callbacks:
  25.         dd      ide_callbacks.end - ide_callbacks       ; strucsize
  26.         dd      0       ; no close function
  27.         dd      0       ; no closemedia function
  28.         dd      ide_querymedia
  29.         dd      ide_read
  30.         dd      ide_write
  31.         dd      0       ; no flush function
  32.         dd      0       ; use default cache size
  33. .end:
  34.  
  35. bd_callbacks:
  36.         dd      bd_callbacks.end - bd_callbacks         ; strucsize
  37.         dd      0       ; no close function
  38.         dd      0       ; no closemedia function
  39.         dd      bd_querymedia
  40.         dd      bd_read_interface
  41.         dd      bd_write_interface
  42.         dd      0       ; no flush function
  43.         dd      0       ; use default cache size
  44. .end:
  45.  
  46. hd0_data        HD_DATA         ?,    0, 1
  47. hd1_data        HD_DATA         ?, 0x10, 2
  48. hd2_data        HD_DATA         ?,    0, 3
  49. hd3_data        HD_DATA         ?, 0x10, 4
  50. endg
  51.  
  52. uglobal
  53. ide_mutex               MUTEX
  54. ide_channel1_mutex      MUTEX
  55. ide_channel2_mutex      MUTEX
  56. endg
  57.  
  58. proc ide_read stdcall uses edi, \
  59.         hd_data, buffer, startsector:qword, numsectors
  60.         ; hd_data = pointer to hd*_data
  61.         ; buffer = pointer to buffer for data
  62.         ; startsector = 64-bit start sector
  63.         ; numsectors = pointer to number of sectors on input,
  64.         ;  must be filled with number of sectors really read
  65. locals
  66. sectors_todo    dd      ?
  67. channel_lock    dd      ?
  68. endl
  69. ; 1. Initialize number of sectors: get number of requested sectors
  70. ; and say that no sectors were read yet.
  71.         mov     ecx, [numsectors]
  72.         mov     eax, [ecx]
  73.         mov     dword [ecx], 0
  74.         mov     [sectors_todo], eax
  75. ; 2. Acquire the global lock.
  76.         mov     ecx, ide_mutex
  77.         call    mutex_lock
  78.         mov     ecx, ide_channel2_mutex
  79.         mov     eax, [hd_data]
  80.         cmp     [eax+HD_DATA.hdbase], 0x1F0
  81.         jne     .IDE_Channel_2
  82.         mov     ecx, ide_channel1_mutex
  83. .IDE_Channel_2:
  84.         mov     [channel_lock], ecx
  85.         call    mutex_lock
  86. ; 3. Convert parameters to the form suitable for worker procedures.
  87. ; Underlying procedures do not know about 64-bit sectors.
  88. ; Worker procedures use global variables and edi for [buffer].
  89.         cmp     dword [startsector+4], 0
  90.         jnz     .fail
  91.         and     [hd_error], 0
  92.         mov     ecx, [hd_data]
  93.         mov     eax, [ecx+HD_DATA.hdbase]
  94.         mov     [hdbase], eax
  95.         mov     eax, [ecx+HD_DATA.hdid]
  96.         mov     [hdid], eax
  97.         mov     eax, [ecx+HD_DATA.hdpos]
  98.         mov     [hdpos], eax
  99.         mov     eax, dword [startsector]
  100.         mov     edi, [buffer]
  101. ; 4. Worker procedures take one sectors per time, so loop over all sectors to read.
  102. .sectors_loop:
  103. ; DMA read is permitted if [allow_dma_access]=1 or 2
  104.         cmp     [allow_dma_access], 2
  105.         ja      .nodma
  106.         cmp     [dma_hdd], 1
  107.         jnz     .nodma
  108.         call    hd_read_dma
  109.         jmp     @f
  110. .nodma:
  111.         call    hd_read_pio
  112. @@:
  113.         cmp     [hd_error], 0
  114.         jnz     .fail
  115.         mov     ecx, [numsectors]
  116.         inc     dword [ecx]     ; one more sector is read
  117.         dec     [sectors_todo]
  118.         jz      .done
  119.         inc     eax
  120.         jnz     .sectors_loop
  121. ; 5. Loop is done, either due to error or because everything is done.
  122. ; Release the global lock and return the corresponding status.
  123. .fail:
  124.         mov     ecx, [channel_lock]
  125.         call    mutex_unlock
  126.         mov     ecx, ide_mutex
  127.         call    mutex_unlock
  128.         or      eax, -1
  129.         ret
  130. .done:
  131.         mov     ecx, [channel_lock]
  132.         call    mutex_unlock
  133.         mov     ecx, ide_mutex
  134.         call    mutex_unlock
  135.         xor     eax, eax
  136.         ret
  137. endp
  138.  
  139. proc ide_write stdcall uses esi edi, \
  140.         hd_data, buffer, startsector:qword, numsectors
  141.         ; hd_data = pointer to hd*_data
  142.         ; buffer = pointer to buffer with data
  143.         ; startsector = 64-bit start sector
  144.         ; numsectors = pointer to number of sectors on input,
  145.         ;  must be filled with number of sectors really written
  146. locals
  147. sectors_todo    dd      ?
  148. channel_lock    dd      ?
  149. endl
  150. ; 1. Initialize number of sectors: get number of requested sectors
  151. ; and say that no sectors were read yet.      
  152.         mov     ecx, [numsectors]
  153.         mov     eax, [ecx]
  154.         mov     dword [ecx], 0
  155.         mov     [sectors_todo], eax
  156. ; 2. Acquire the global lock.
  157.         mov     ecx, ide_mutex
  158.         call    mutex_lock
  159.         mov     ecx, ide_channel2_mutex
  160.         mov     eax, [hd_data]
  161.         cmp     [eax+HD_DATA.hdbase], 0x1F0
  162.         jne     .IDE_Channel_2
  163.         mov     ecx, ide_channel1_mutex
  164. .IDE_Channel_2:
  165.         mov     [channel_lock], ecx
  166.         call    mutex_lock
  167. ; 3. Convert parameters to the form suitable for worker procedures.
  168. ; Underlying procedures do not know about 64-bit sectors.
  169. ; Worker procedures use global variables and esi for [buffer].
  170.         cmp     dword [startsector+4], 0
  171.         jnz     .fail
  172.         and     [hd_error], 0
  173.         mov     ecx, [hd_data]
  174.         mov     eax, [ecx+HD_DATA.hdbase]
  175.         mov     [hdbase], eax
  176.         mov     eax, [ecx+HD_DATA.hdid]
  177.         mov     [hdid], eax
  178.         mov     eax, [ecx+HD_DATA.hdpos]
  179.         mov     [hdpos], eax
  180.         mov     esi, [buffer]
  181.         lea     edi, [startsector]
  182.         mov     [cache_chain_ptr], edi
  183. ; 4. Worker procedures take max 16 sectors per time,
  184. ; loop until all sectors will be processed.
  185. .sectors_loop:
  186.         mov     ecx, 16
  187.         cmp     ecx, [sectors_todo]
  188.         jbe     @f
  189.         mov     ecx, [sectors_todo]
  190. @@:
  191.         mov     [cache_chain_size], cl
  192. ; DMA write is permitted only if [allow_dma_access]=1
  193.         cmp     [allow_dma_access], 2
  194.         jae     .nodma
  195.         cmp     [dma_hdd], 1
  196.         jnz     .nodma
  197.         call    cache_write_dma
  198.         jmp     .common
  199. .nodma:
  200.         mov     [cache_chain_size], 1
  201.         call    cache_write_pio
  202. .common:
  203.         cmp     [hd_error], 0
  204.         jnz     .fail
  205.         movzx   ecx, [cache_chain_size]
  206.         mov     eax, [numsectors]
  207.         add     [eax], ecx
  208.         sub     [sectors_todo], ecx
  209.         jz      .done
  210.         add     [edi], ecx
  211.         jc      .fail
  212.         shl     ecx, 9
  213.         add     esi, ecx
  214.         jmp     .sectors_loop
  215. ; 5. Loop is done, either due to error or because everything is done.
  216. ; Release the global lock and return the corresponding status.
  217. .fail:
  218.         mov     ecx, [channel_lock]
  219.         call    mutex_unlock
  220.         mov     ecx, ide_mutex
  221.         call    mutex_unlock
  222.         or      eax, -1
  223.         ret
  224. .done:
  225.         mov     ecx, [channel_lock]
  226.         call    mutex_unlock
  227.         mov     ecx, ide_mutex
  228.         call    mutex_unlock
  229.         xor     eax, eax
  230.         ret
  231. endp
  232.  
  233. ; This is a stub.
  234. proc ide_querymedia stdcall, hd_data, mediainfo
  235.         mov     eax, [mediainfo]
  236.         mov     [eax+DISKMEDIAINFO.Flags], 0
  237.         mov     [eax+DISKMEDIAINFO.SectorSize], 512
  238.         or      dword [eax+DISKMEDIAINFO.Capacity], 0xFFFFFFFF
  239.         or      dword [eax+DISKMEDIAINFO.Capacity+4], 0xFFFFFFFF
  240.         xor     eax, eax
  241.         ret
  242. endp
  243.  
  244. proc bd_read_interface stdcall uses edi, \
  245.         userdata, buffer, startsector:qword, numsectors
  246.         ; userdata = old [hdpos] = 80h + index in NumBiosDisks
  247.         ; buffer = pointer to buffer for data
  248.         ; startsector = 64-bit start sector
  249.         ; numsectors = pointer to number of sectors on input,
  250.         ;  must be filled with number of sectors really read
  251. locals
  252. sectors_todo    dd      ?
  253. endl
  254. ; 1. Initialize number of sectors: get number of requested sectors
  255. ; and say that no sectors were read yet.
  256.         mov     ecx, [numsectors]
  257.         mov     eax, [ecx]
  258.         mov     dword [ecx], 0
  259.         mov     [sectors_todo], eax
  260. ; 2. Acquire the global lock.
  261.         mov     ecx, ide_mutex
  262.         call    mutex_lock
  263. ; 3. Convert parameters to the form suitable for worker procedures.
  264. ; Underlying procedures do not know about 64-bit sectors.
  265. ; Worker procedures use global variables and edi for [buffer].
  266.         cmp     dword [startsector+4], 0
  267.         jnz     .fail
  268.         and     [hd_error], 0
  269.         mov     eax, [userdata]
  270.         mov     [hdpos], eax
  271.         mov     eax, dword [startsector]
  272.         mov     edi, [buffer]
  273. ; 4. Worker procedures take one sectors per time, so loop over all sectors to read.
  274. .sectors_loop:
  275.         call    bd_read
  276.         cmp     [hd_error], 0
  277.         jnz     .fail
  278.         mov     ecx, [numsectors]
  279.         inc     dword [ecx]     ; one more sector is read
  280.         dec     [sectors_todo]
  281.         jz      .done
  282.         inc     eax
  283.         jnz     .sectors_loop
  284. ; 5. Loop is done, either due to error or because everything is done.
  285. ; Release the global lock and return the corresponding status.
  286. .fail:
  287.         mov     ecx, ide_mutex
  288.         call    mutex_unlock
  289.         or      eax, -1
  290.         ret
  291. .done:
  292.         mov     ecx, ide_mutex
  293.         call    mutex_unlock
  294.         xor     eax, eax
  295.         ret
  296. endp
  297.  
  298. proc bd_write_interface stdcall uses esi edi, \
  299.         userdata, buffer, startsector:qword, numsectors
  300.         ; userdata = old [hdpos] = 80h + index in NumBiosDisks
  301.         ; buffer = pointer to buffer with data
  302.         ; startsector = 64-bit start sector
  303.         ; numsectors = pointer to number of sectors on input,
  304.         ;  must be filled with number of sectors really written
  305. locals
  306. sectors_todo    dd      ?
  307. endl
  308. ; 1. Initialize number of sectors: get number of requested sectors
  309. ; and say that no sectors were read yet.      
  310.         mov     ecx, [numsectors]
  311.         mov     eax, [ecx]
  312.         mov     dword [ecx], 0
  313.         mov     [sectors_todo], eax
  314. ; 2. Acquire the global lock.
  315.         mov     ecx, ide_mutex
  316.         call    mutex_lock
  317. ; 3. Convert parameters to the form suitable for worker procedures.
  318. ; Underlying procedures do not know about 64-bit sectors.
  319. ; Worker procedures use global variables and esi for [buffer].
  320.         cmp     dword [startsector+4], 0
  321.         jnz     .fail
  322.         and     [hd_error], 0
  323.         mov     eax, [userdata]
  324.         mov     [hdpos], eax
  325.         mov     esi, [buffer]
  326.         lea     edi, [startsector]
  327.         mov     [cache_chain_ptr], edi
  328. ; 4. Worker procedures take max 16 sectors per time,
  329. ; loop until all sectors will be processed.
  330. .sectors_loop:
  331.         mov     ecx, 16
  332.         cmp     ecx, [sectors_todo]
  333.         jbe     @f
  334.         mov     ecx, [sectors_todo]
  335. @@:
  336.         mov     [cache_chain_size], cl
  337.         call    bd_write_cache_chain
  338.         cmp     [hd_error], 0
  339.         jnz     .fail
  340.         movzx   ecx, [cache_chain_size]
  341.         mov     eax, [numsectors]
  342.         add     [eax], ecx
  343.         sub     [sectors_todo], ecx
  344.         jz      .done
  345.         add     [edi], ecx
  346.         jc      .fail
  347.         shl     ecx, 9
  348.         add     esi, ecx
  349.         jmp     .sectors_loop
  350. ; 5. Loop is done, either due to error or because everything is done.
  351. ; Release the global lock and return the corresponding status.
  352. .fail:
  353.         mov     ecx, ide_mutex
  354.         call    mutex_unlock
  355.         or      eax, -1
  356.         ret
  357. .done:
  358.         mov     ecx, ide_mutex
  359.         call    mutex_unlock
  360.         xor     eax, eax
  361.         ret
  362. endp
  363.  
  364. ; This is a stub.
  365. proc bd_querymedia stdcall, hd_data, mediainfo
  366.         mov     eax, [mediainfo]
  367.         mov     [eax+DISKMEDIAINFO.Flags], 0
  368.         mov     [eax+DISKMEDIAINFO.SectorSize], 512
  369.         or      dword [eax+DISKMEDIAINFO.Capacity], 0xFFFFFFFF
  370.         or      dword [eax+DISKMEDIAINFO.Capacity+4], 0xFFFFFFFF
  371.         xor     eax, eax
  372.         ret
  373. endp
  374.  
  375. ;-----------------------------------------------------------------------------
  376. align 4
  377. ; input: eax = sector, edi -> buffer
  378. ; output: edi = edi + 512
  379. hd_read_pio:
  380.         push    eax edx
  381.  
  382. ; Select the desired drive
  383.         mov     edx, [hdbase]
  384.         add     edx, 6   ;адрес регистра головок
  385.         mov     al, byte [hdid]
  386.         add     al, 128+64+32
  387.         out     dx, al; номер головки/номер диска
  388.        
  389.         call    wait_for_hd_idle
  390.         cmp     [hd_error], 0
  391.         jne     hd_read_error
  392.        
  393. ; ATA with 28 or 48 bit for sector number?
  394.         mov     eax, [esp+4]
  395.         cmp     eax, 0x10000000
  396.         jae     .lba48
  397. ;--------------------------------------
  398. .lba28:
  399.         pushfd
  400.         cli
  401.         xor     eax, eax
  402.         mov     edx, [hdbase]
  403.         inc     edx
  404.         out     dx, al ; ATA Features регистр "особенностей"
  405.         inc     edx
  406.         inc     eax
  407.         out     dx, al ; ATA Sector Counter счётчик секторов
  408.         inc     edx
  409.         mov     eax, [esp+4+4]
  410.         out     dx, al ; LBA Low LBA (7:0)
  411.         shr     eax, 8
  412.         inc     edx
  413.         out     dx, al ; LBA Mid LBA (15:8)
  414.         shr     eax, 8
  415.         inc     edx
  416.         out     dx, al ; LBA High LBA (23:16)
  417.         shr     eax, 8
  418.         inc     edx
  419.         and     al, 1+2+4+8 ; LBA (27:24)
  420.         add     al, byte [hdid]
  421.         add     al, 128+64+32
  422.         out     dx, al ; номер головки/номер диска
  423.         inc     edx
  424.         mov     al, 20h ; READ SECTOR(S)
  425.         out     dx, al ; ATACommand регистр команд
  426.         popfd
  427.         jmp     .continue
  428. ;--------------------------------------
  429. .lba48:
  430.         pushfd
  431.         cli
  432.         xor     eax, eax
  433.         mov     edx, [hdbase]
  434.         inc     edx
  435.         out     dx, al ; Features Previous Reserved
  436.         out     dx, al ; Features Current Reserved
  437.         inc     edx
  438.         out     dx, al ; Sector Count Previous Sector count (15:8)
  439.         inc     eax
  440.         out     dx, al ; Sector Count Current Sector count (7:0)
  441.         inc     edx
  442.         mov     eax, [esp+4+4]
  443.         rol     eax, 8
  444.         out     dx, al ; LBA Low Previous LBA (31:24)
  445.         xor     eax, eax ; because only 32 bit cache
  446.         inc     edx
  447.         out     dx, al ; LBA Mid Previous LBA (39:32)
  448.         inc     edx
  449.         out     dx, al ; LBA High Previous LBA (47:40)
  450.         sub     edx, 2
  451.         mov     eax, [esp+4+4]
  452.         out     dx, al ; LBA Low Current LBA (7:0)
  453.         shr     eax, 8
  454.         inc     edx
  455.         out     dx, al ; LBA Mid Current LBA (15:8)
  456.         shr     eax, 8
  457.         inc     edx
  458.         out     dx, al ; LBA High Current LBA (23:16)
  459.         inc     edx
  460.         mov     al, byte [hdid]
  461.         add     al, 128+64+32
  462.         out     dx, al ; номер головки/номер диска
  463.         inc     edx
  464.         mov     al, 24h ; READ SECTOR(S) EXT
  465.         out     dx, al ; ATACommand регистр команд
  466.         popfd
  467. ;--------------------------------------
  468. .continue:
  469.         call    wait_for_sector_buffer
  470.  
  471.         cmp     [hd_error], 0
  472.         jne     hd_read_error
  473.  
  474.         pushfd
  475.         cli
  476.  
  477.         mov     ecx, 256
  478.         mov     edx, [hdbase]
  479.         cld
  480.         rep insw
  481.         popfd
  482.  
  483.         pop     edx eax
  484.         ret
  485. ;-----------------------------------------------------------------------------
  486. align 4
  487. ; edi -> sector, esi -> data
  488. cache_write_pio:
  489. ; Select the desired drive
  490.         mov     edx, [hdbase]
  491.         add     edx, 6   ;адрес регистра головок
  492.         mov     al, byte [hdid]
  493.         add     al, 128+64+32
  494.         out     dx, al ; номер головки/номер диска
  495.  
  496.         call    wait_for_hd_idle
  497.         cmp     [hd_error], 0
  498.         jne     hd_write_error
  499.  
  500. ; ATA with 28 or 48 bit for sector number?
  501.         mov     eax, [edi]
  502.         cmp     eax, 0x10000000
  503.         jae     .lba48
  504. ;--------------------------------------
  505. .lba28:
  506.         pushfd
  507.         cli
  508.         xor     eax, eax
  509.         mov     edx, [hdbase]
  510.         inc     edx
  511.         out     dx, al ; ATA Features регистр "особенностей"
  512.         inc     edx
  513.         inc     eax
  514.         out     dx, al ; ATA Sector Counter счётчик секторов
  515.         inc     edx
  516.         mov     eax, [edi]      ; eax = sector to write
  517.         out     dx, al ; LBA Low LBA (7:0)
  518.         shr     eax, 8
  519.         inc     edx
  520.         out     dx, al ; LBA Mid LBA (15:8)
  521.         shr     eax, 8
  522.         inc     edx
  523.         out     dx, al ; LBA High LBA (23:16)
  524.         shr     eax, 8
  525.         inc     edx
  526.         and     al, 1+2+4+8 ; LBA (27:24)
  527.         add     al, byte [hdid]
  528.         add     al, 128+64+32
  529.         out     dx, al ; номер головки/номер диска
  530.         inc     edx
  531.         mov     al, 30h ; WRITE SECTOR(S)
  532.         out     dx, al ; ATACommand регистр команд
  533.         popfd
  534.         jmp     .continue
  535. ;--------------------------------------
  536. .lba48:
  537.         pushfd
  538.         cli
  539.         xor     eax, eax
  540.         mov     edx, [hdbase]
  541.         inc     edx
  542.         out     dx, al ; Features Previous Reserved
  543.         out     dx, al ; Features Current Reserved
  544.         inc     edx
  545.         out     dx, al ; Sector Count Previous Sector count (15:8)
  546.         inc     eax
  547.         out     dx, al ; Sector Count Current Sector count (7:0)
  548.         inc     edx
  549.         mov     eax, [edi]
  550.         rol     eax, 8
  551.         out     dx, al ; LBA Low Previous LBA (31:24)
  552.         xor     eax, eax ; because only 32 bit cache
  553.         inc     edx
  554.         out     dx, al ; LBA Mid Previous LBA (39:32)
  555.         inc     edx
  556.         out     dx, al ; LBA High Previous LBA (47:40)
  557.         sub     edx, 2
  558.         mov     eax, [edi]
  559.         out     dx, al ; LBA Low Current LBA (7:0)
  560.         shr     eax, 8
  561.         inc     edx
  562.         out     dx, al ; LBA Mid Current LBA (15:8)
  563.         shr     eax, 8
  564.         inc     edx
  565.         out     dx, al ; LBA High Current LBA (23:16)
  566.         inc     edx
  567.         mov     al, byte [hdid]
  568.         add     al, 128+64+32
  569.         out     dx, al ; номер головки/номер диска
  570.         inc     edx
  571.         mov     al, 34h ; WRITE SECTOR(S) EXT
  572.         out     dx, al ; ATACommand регистр команд
  573.         popfd
  574. ;--------------------------------------
  575. .continue:
  576.         call    wait_for_sector_buffer
  577.  
  578.         cmp     [hd_error], 0
  579.         jne     hd_write_error
  580.  
  581.         push    ecx esi
  582.  
  583.         pushfd
  584.         cli
  585.         mov     ecx, 256
  586.         mov     edx, [hdbase]
  587.         cld
  588.         rep outsw
  589.         popfd
  590.  
  591.         pop     esi ecx
  592.         ret
  593. ;-----------------------------------------------------------------------------
  594. align 4
  595. save_hd_wait_timeout:
  596.         push    eax
  597.         mov     eax, [timer_ticks]
  598.         add     eax, 300        ; 3 sec timeout
  599.         mov     [hd_wait_timeout], eax
  600.         pop     eax
  601.         ret
  602. ;-----------------------------------------------------------------------------
  603. align 4
  604. check_hd_wait_timeout:
  605.         push    eax
  606.         mov     eax, [hd_wait_timeout]
  607.         cmp     [timer_ticks], eax
  608.         jg      hd_timeout_error
  609.  
  610.         pop     eax
  611.         mov     [hd_error], 0
  612.         ret
  613. ;-----------------------------------------------------------------------------
  614. hd_timeout_error:
  615.         if lang eq sp
  616.         DEBUGF 1,"K : FS - HD tiempo de espera agotado\n"
  617.         else
  618.         DEBUGF 1,"K : FS - HD timeout\n"
  619.         end if
  620.         mov     [hd_error], 1
  621.         pop     eax
  622.         ret
  623. ;-----------------------------------------------------------------------------
  624. hd_read_error:
  625.         if lang eq sp
  626.         DEBUGF 1,"K : FS - HD error de lectura\n"
  627.         else
  628.         DEBUGF 1,"K : FS - HD read error\n"
  629.         end if
  630.         pop     edx eax
  631.         ret
  632. ;-----------------------------------------------------------------------------
  633. hd_write_error_dma:
  634.         pop     esi
  635. hd_write_error:
  636.         if lang eq sp
  637.         DEBUGF 1,"K : FS - HD error de escritura\n"
  638.         else
  639.         DEBUGF 1,"K : FS - HD write error\n"
  640.         end if
  641.         ret
  642. ;-----------------------------------------------------------------------------
  643. hd_lba_error:
  644.         if lang eq sp
  645.         DEBUGF 1,"K : FS - HD error en LBA\n"
  646.         else
  647.         DEBUGF 1,"K : FS - HD LBA error\n"
  648.         end if
  649.         jmp     LBA_read_ret
  650. ;-----------------------------------------------------------------------------
  651. align 4
  652. wait_for_hd_idle:
  653.         push    eax edx
  654.  
  655.         call    save_hd_wait_timeout
  656.  
  657.         mov     edx, [hdbase]
  658.         add     edx, 0x7
  659. ;--------------------------------------
  660. align 4
  661. wfhil1:
  662.         call    check_hd_wait_timeout
  663.         cmp     [hd_error], 0
  664.         jne     @f
  665.  
  666.         in      al, dx
  667.         test    al, 128
  668.         jnz     wfhil1
  669.  
  670. @@:
  671.         pop     edx eax
  672.         ret
  673. ;-----------------------------------------------------------------------------
  674. align 4
  675. wait_for_sector_buffer:
  676.         push    eax edx
  677.  
  678.         mov     edx, [hdbase]
  679.         add     edx, 0x7
  680.  
  681.         call    save_hd_wait_timeout
  682. ;--------------------------------------
  683. align 4
  684. hdwait_sbuf:                  ; wait for sector buffer to be ready
  685.         call    check_hd_wait_timeout
  686.         cmp     [hd_error], 0
  687.         jne     @f
  688.  
  689.         in      al, dx
  690.         test    al, 8
  691.         jz      hdwait_sbuf
  692.  
  693.         mov     [hd_error], 0
  694.  
  695.         cmp     [hd_setup], 1   ; do not mark error for setup request
  696.         je      buf_wait_ok
  697.  
  698.         test    al, 1           ; previous command ended up with an error
  699.         jz      buf_wait_ok
  700. @@:
  701.         mov     [hd_error], 1
  702.  
  703. buf_wait_ok:
  704.         pop     edx eax
  705.         ret
  706. ;-----------------------------------------------------------------------------
  707. irq14_num equ byte 14
  708. irq15_num equ byte 15
  709. ;-----------------------------------------------------------------------------
  710. align 4
  711. wait_for_sector_dma_ide0:
  712.         push    eax
  713.         push    edx
  714.         call    save_hd_wait_timeout
  715. ;--------------------------------------
  716. align 4
  717. .wait:
  718.         call    change_task
  719.         cmp     [IDE_common_irq_param], irq14_num
  720.         jnz     .done
  721.  
  722.         call    check_hd_wait_timeout
  723.         cmp     [hd_error], 0
  724.         jz      .wait
  725.  
  726.         mov     [IDE_common_irq_param], 0
  727.         mov     dx, [IDEContrRegsBaseAddr]
  728.         mov     al, 0
  729.         out     dx, al
  730. .done:
  731.         pop     edx
  732.         pop     eax
  733.         ret
  734. ;-----------------------------------------------------------------------------
  735. align 4
  736. wait_for_sector_dma_ide1:
  737.         push    eax
  738.         push    edx
  739.         call    save_hd_wait_timeout
  740. ;--------------------------------------
  741. align 4
  742. .wait:
  743.         call    change_task
  744.         cmp     [IDE_common_irq_param], irq15_num
  745.         jnz     .done
  746.  
  747.         call    check_hd_wait_timeout
  748.         cmp     [hd_error], 0
  749.         jz      .wait
  750.  
  751.         mov     [IDE_common_irq_param], 0
  752.         mov     dx, [IDEContrRegsBaseAddr]
  753.         add     dx, 8
  754.         mov     al, 0
  755.         out     dx, al
  756. .done:
  757.         pop     edx
  758.         pop     eax
  759.         ret
  760. ;-----------------------------------------------------------------------------
  761. iglobal
  762. align 4
  763. ; note that IDE descriptor table must be 4-byte aligned and do not cross 4K boundary
  764. IDE_descriptor_table:
  765.         dd IDE_DMA
  766.         dw 0x2000
  767.         dw 0x8000
  768.  
  769. dma_cur_sector  dd not 40h
  770. dma_hdpos       dd 0
  771. IDE_common_irq_param db 0
  772. endg
  773. ;-----------------------------------------------------------------------------
  774. uglobal
  775. ; all uglobals are zeroed at boot
  776. dma_process         dd 0
  777. dma_slot_ptr        dd 0
  778. cache_chain_pos     dd 0
  779. cache_chain_ptr     dd 0
  780. cache_chain_size    db 0
  781. cache_chain_started db 0
  782. dma_task_switched   db 0
  783. dma_hdd             db 0
  784. allow_dma_access    db 0
  785. endg
  786. ;-----------------------------------------------------------------------------
  787. align 4
  788. IDE_common_irq_handler:
  789.         pushfd
  790.         cli
  791.         pushad
  792.  
  793.         xor     ebx, ebx
  794.         mov     eax, IDE_common_irq_param
  795.         cmp     [eax], irq15_num
  796.         mov     [eax], ebx
  797.         je      @f
  798. ;--------------------------------------
  799.         mov     dx, [IDEContrRegsBaseAddr]
  800.         mov     al, 0
  801.         out     dx, al
  802.         jmp     .end
  803. ;--------------------------------------
  804. align 4
  805. @@:
  806.         mov     dx, [IDEContrRegsBaseAddr]
  807.         add     dx, 8
  808.         mov     al, 0
  809.         out     dx, al
  810. ;--------------------------------------
  811. align 4
  812. .end:
  813.         popad
  814.         popfd
  815.         mov     al, 1
  816.         ret
  817. ;-----------------------------------------------------------------------------
  818. align 4
  819. hd_read_dma:
  820.         push    eax
  821.         push    edx
  822.         mov     edx, [dma_hdpos]
  823.         cmp     edx, [hdpos]
  824.         jne     .notread
  825.         mov     edx, [dma_cur_sector]
  826.         cmp     eax, edx
  827.         jb      .notread
  828.         add     edx, 15
  829.         cmp     [esp+4], edx
  830.         ja      .notread
  831.         mov     eax, [esp+4]
  832.         sub     eax, [dma_cur_sector]
  833.         shl     eax, 9
  834.         add     eax, (OS_BASE+IDE_DMA)
  835.         push    ecx esi
  836.         mov     esi, eax
  837.  
  838.         mov     ecx, 512/4
  839.         cld
  840.         rep movsd
  841.         pop     esi ecx
  842.         pop     edx
  843.         pop     eax
  844.         ret
  845. .notread:
  846.         mov     eax, IDE_descriptor_table
  847.         mov     dword [eax], IDE_DMA
  848.         mov     word [eax+4], 0x2000
  849.         sub     eax, OS_BASE
  850.         mov     dx, [IDEContrRegsBaseAddr]
  851.         cmp     [hdbase], 0x1F0
  852.         jz      @f
  853.         add     edx, 8
  854. @@:
  855.         push    edx
  856.         add     edx, 4
  857.         out     dx, eax
  858.         pop     edx
  859.         mov     al, 0
  860.         out     dx, al
  861.         add     edx, 2
  862.         mov     al, 6
  863.         out     dx, al
  864.  
  865. ; Select the desired drive
  866.         mov     edx, [hdbase]
  867.         add     edx, 6   ; адрес регистра головок
  868.         mov     al, byte [hdid]
  869.         add     al, 128+64+32
  870.         out     dx, al ; номер головки/номер диска
  871.  
  872.         call    wait_for_hd_idle
  873.         cmp     [hd_error], 0
  874.         jnz     hd_read_error
  875.  
  876. ; ATA with 28 or 48 bit for sector number?
  877.         mov     eax, [esp+4]
  878. ; -10h because the PreCache hits the boundary between lba28 and lba48
  879. ; 10h = 16  - size of PreCache
  880.         cmp     eax, 0x10000000-10h
  881.         jae     .lba48
  882. ;--------------------------------------
  883. .lba28:
  884.         pushfd
  885.         cli
  886.         xor     eax, eax
  887.         mov     edx, [hdbase]
  888.         inc     edx
  889.         out     dx, al ; ATA Features регистр "особенностей"
  890.         inc     edx
  891.         mov     eax, 10h ; Sector Counter = 16 ; PreCache
  892.         out     dx, al ; ATA Sector Counter счётчик секторов
  893.         inc     edx
  894.         mov     eax, [esp+4+4]
  895.         out     dx, al ; LBA Low LBA (7:0)
  896.         shr     eax, 8
  897.         inc     edx
  898.         out     dx, al ; LBA Mid LBA (15:8)
  899.         shr     eax, 8
  900.         inc     edx
  901.         out     dx, al ; LBA High LBA (23:16)
  902.         shr     eax, 8
  903.         inc     edx
  904.         and     al, 0xF ; LBA (27:24)
  905.         add     al, byte [hdid]
  906.         add     al, 11100000b
  907.         out     dx, al ; номер головки/номер диска
  908.         inc     edx
  909.         mov     al, 0xC8 ; READ DMA
  910.         out     dx, al ; ATACommand регистр команд
  911.         jmp     .continue
  912. ;--------------------------------------
  913. .lba48:
  914.         pushfd
  915.         cli
  916.         xor     eax, eax
  917.         mov     edx, [hdbase]
  918.         inc     edx
  919.         out     dx, al ; Features Previous Reserved
  920.         out     dx, al ; Features Current Reserved
  921.         inc     edx
  922.         out     dx, al ; Sector Count Previous Sector count (15:8)
  923.         mov     eax, 10h ; Sector Counter = 16 PreCache
  924.         out     dx, al ; Sector Count Current Sector count (7:0)
  925.         inc     edx
  926.         mov     eax, [esp+4+4]
  927.         rol     eax, 8
  928.         out     dx, al ; LBA Low Previous LBA (31:24)
  929.         xor     eax, eax ; because only 32 bit cache
  930.         inc     edx
  931.         out     dx, al ; LBA Mid Previous LBA (39:32)
  932.         inc     edx
  933.         out     dx, al ; LBA High Previous LBA (47:40)
  934.         sub     edx, 2
  935.         mov     eax, [esp+4+4]
  936.         out     dx, al ; LBA Low Current LBA (7:0)
  937.         shr     eax, 8
  938.         inc     edx
  939.         out     dx, al ; LBA Mid Current LBA (15:8)
  940.         shr     eax, 8
  941.         inc     edx
  942.         out     dx, al ; LBA High Current LBA (23:16)
  943.         inc     edx
  944.         mov     al, byte [hdid]
  945.         add     al, 128+64+32
  946.         out     dx, al ; номер головки/номер диска
  947.         inc     edx
  948.         mov     al, 25h ; READ DMA EXT
  949.         out     dx, al ; ATACommand регистр команд
  950. ;--------------------------------------
  951. .continue:
  952.         mov     dx, [IDEContrRegsBaseAddr]
  953.         mov     eax, [hd_address_table]
  954.         cmp     [hdbase], eax ; 0x1F0
  955.         jz      @f
  956.         add     dx, 8
  957. @@:
  958.         mov     al, 9
  959.         out     dx, al
  960.         mov     eax, [CURRENT_TASK]
  961.         mov     [dma_process], eax
  962.         mov     eax, [TASK_BASE]
  963.         mov     [dma_slot_ptr], eax
  964.         mov     eax, [hd_address_table]
  965.         cmp     [hdbase], eax ; 0x1F0
  966.         jnz     .ide1
  967.  
  968.         mov     [IDE_common_irq_param], irq14_num
  969.         jmp     @f
  970. .ide1:
  971.         mov     [IDE_common_irq_param], irq15_num
  972. @@:
  973.         popfd
  974.         mov     eax, [hd_address_table]
  975.         cmp     [hdbase], eax ; 0x1F0
  976.         jnz     .wait_ide1
  977.         call    wait_for_sector_dma_ide0
  978.         jmp     @f
  979. .wait_ide1:
  980.         call    wait_for_sector_dma_ide1
  981. @@:
  982.         cmp     [hd_error], 0
  983.         jnz     hd_read_error
  984.         mov     eax, [hdpos]
  985.         mov     [dma_hdpos], eax
  986.         pop     edx
  987.         pop     eax
  988.         mov     [dma_cur_sector], eax
  989.         jmp     hd_read_dma
  990. ;-----------------------------------------------------------------------------
  991. cache_write_dma:
  992.         mov     eax, [cache_chain_ptr]
  993.         push    esi
  994.         mov     eax, IDE_descriptor_table
  995.         mov     edx, eax
  996.         pusha
  997.         mov     edi, (OS_BASE+IDE_DMA)
  998.         mov     dword [edx], IDE_DMA
  999.         movzx   ecx, [cache_chain_size]
  1000.         shl     ecx, 9
  1001.         mov     word [edx+4], cx
  1002.         shr     ecx, 2
  1003.         cld
  1004.         rep movsd
  1005.         popa
  1006.         sub     eax, OS_BASE
  1007.         mov     dx, [IDEContrRegsBaseAddr]
  1008.         cmp     [hdbase], 0x1F0
  1009.         jz      @f
  1010.         add     edx, 8
  1011. @@:
  1012.         push    edx
  1013.         add     edx, 4
  1014.         out     dx, eax
  1015.         pop     edx
  1016.         mov     al, 0
  1017.         out     dx, al
  1018.         add     edx, 2
  1019.         mov     al, 6
  1020.         out     dx, al
  1021.  
  1022. ; Select the desired drive
  1023.         mov     edx, [hdbase]
  1024.         add     edx, 6   ; адрес регистра головок
  1025.         mov     al, byte [hdid]
  1026.         add     al, 128+64+32
  1027.         out     dx, al ; номер головки/номер диска
  1028.  
  1029.         call    wait_for_hd_idle
  1030.         cmp     [hd_error], 0
  1031.         jnz     hd_write_error_dma
  1032.  
  1033. ; ATA with 28 or 48 bit for sector number?
  1034.         mov     esi, [cache_chain_ptr]
  1035.         mov     eax, [esi]
  1036. ; -40h because the PreCache hits the boundary between lba28 and lba48
  1037. ; 40h = 64  - the maximum number of sectors to be written for one command
  1038.         cmp     eax, 0x10000000-40h
  1039.         jae     .lba48
  1040. ;--------------------------------------
  1041. .lba28:
  1042.         pushfd
  1043.         cli
  1044.         xor     eax, eax
  1045.         mov     edx, [hdbase]
  1046.         inc     edx
  1047.         out     dx, al ; ATA Features регистр "особенностей"
  1048.         inc     edx
  1049.         mov     al, [cache_chain_size] ; Sector Counter
  1050.         out     dx, al ; ATA Sector Counter счётчик секторов
  1051.         inc     edx
  1052.         mov     eax, [esi]
  1053.         out     dx, al ; LBA Low LBA (7:0)
  1054.         shr     eax, 8
  1055.         inc     edx
  1056.         out     dx, al ; LBA Mid LBA (15:8)
  1057.         shr     eax, 8
  1058.         inc     edx
  1059.         out     dx, al ; LBA High LBA (23:16)
  1060.         shr     eax, 8
  1061.         inc     edx
  1062.         and     al, 0xF ; LBA (27:24)
  1063.         add     al, byte [hdid]
  1064.         add     al, 11100000b
  1065.         out     dx, al ; номер головки/номер диска
  1066.         inc     edx
  1067.         mov     al, 0xCA ; WRITE DMA
  1068.         out     dx, al ; ATACommand регистр команд
  1069.         jmp     .continue
  1070. ;--------------------------------------
  1071. .lba48:
  1072.         pushfd
  1073.         cli
  1074.         xor     eax, eax
  1075.         mov     edx, [hdbase]
  1076.         inc     edx
  1077.         out     dx, al ; Features Previous Reserved
  1078.         out     dx, al ; Features Current Reserved
  1079.         inc     edx
  1080.         out     dx, al ; Sector Count Previous Sector count (15:8)
  1081.         mov     al, [cache_chain_size] ; Sector Counter
  1082.         out     dx, al ; Sector Count Current Sector count (7:0)
  1083.         inc     edx
  1084.         mov     eax, [esi]
  1085.         rol     eax, 8
  1086.         out     dx, al ; LBA Low Previous LBA (31:24)
  1087.         xor     eax, eax ; because only 32 bit cache
  1088.         inc     edx
  1089.         out     dx, al ; LBA Mid Previous LBA (39:32)
  1090.         inc     edx
  1091.         out     dx, al ; LBA High Previous LBA (47:40)
  1092.         sub     edx, 2
  1093.         mov     eax, [esi]
  1094.         out     dx, al ; LBA Low Current LBA (7:0)
  1095.         shr     eax, 8
  1096.         inc     edx
  1097.         out     dx, al ; LBA Mid Current LBA (15:8)
  1098.         shr     eax, 8
  1099.         inc     edx
  1100.         out     dx, al ; LBA High Current LBA (23:16)
  1101.         inc     edx
  1102.         mov     al, byte [hdid]
  1103.         add     al, 128+64+32
  1104.         out     dx, al ; номер головки/номер диска
  1105.         inc     edx
  1106.         mov     al, 35h ; WRITE DMA EXT
  1107.         out     dx, al ; ATACommand регистр команд
  1108. ;--------------------------------------
  1109. .continue:
  1110.         mov     dx, [IDEContrRegsBaseAddr]
  1111.         mov     eax, [hd_address_table]
  1112.         cmp     [hdbase], eax ; 0x1F0
  1113.         jz      @f
  1114.         add     dx, 8
  1115. @@:
  1116.         mov     al, 1
  1117.         out     dx, al
  1118.         mov     eax, [CURRENT_TASK]
  1119.         mov     [dma_process], eax
  1120.         mov     eax, [TASK_BASE]
  1121.         mov     [dma_slot_ptr], eax
  1122.         mov     eax, [hd_address_table]
  1123.         cmp     [hdbase], eax ; 0x1F0
  1124.         jnz     .ide1
  1125.  
  1126.         mov     [IDE_common_irq_param], irq14_num
  1127.         jmp     @f
  1128. .ide1:
  1129.         mov     [IDE_common_irq_param], irq15_num
  1130. @@:
  1131.         popfd
  1132.         mov     [dma_cur_sector], not 0x40
  1133.         mov     eax, [hd_address_table]
  1134.         cmp     [hdbase], eax ; 0x1F0
  1135.         jnz     .wait_ide1
  1136.         call    wait_for_sector_dma_ide0
  1137.         jmp     @f
  1138. .wait_ide1:
  1139.         call    wait_for_sector_dma_ide1
  1140. @@:
  1141.         cmp     [hd_error], 0
  1142.         jnz     hd_write_error_dma
  1143.         pop     esi
  1144.         ret
  1145. ;-----------------------------------------------------------------------------
  1146. uglobal
  1147. IDE_Interrupt   dw ?
  1148. IDEContrRegsBaseAddr         dw ?
  1149. IDEContrProgrammingInterface dw ?
  1150. IDE_BAR0_val    dw ?
  1151. IDE_BAR1_val    dw ?
  1152. IDE_BAR2_val    dw ?
  1153. IDE_BAR3_val    dw ?
  1154. endg
  1155. ;-----------------------------------------------------------------------------
  1156. ; \begin{diamond}
  1157. uglobal
  1158. bios_hdpos      dd 0       ; 0 is invalid value for [hdpos]
  1159. bios_cur_sector dd ?
  1160. bios_read_len   dd ?
  1161. endg
  1162. ;-----------------------------------------------------------------------------
  1163. align 4
  1164. bd_read:
  1165.         push    eax
  1166.         push    edx
  1167.         mov     edx, [bios_hdpos]
  1168.         cmp     edx, [hdpos]
  1169.         jne     .notread
  1170.         mov     edx, [bios_cur_sector]
  1171.         cmp     eax, edx
  1172.         jb      .notread
  1173.         add     edx, [bios_read_len]
  1174.         dec     edx
  1175.         cmp     eax, edx
  1176.         ja      .notread
  1177.         sub     eax, [bios_cur_sector]
  1178.         shl     eax, 9
  1179.         add     eax, (OS_BASE+0x9A000)
  1180.         push    ecx esi
  1181.         mov     esi, eax
  1182.         mov     ecx, 512/4
  1183.         cld
  1184.         rep movsd
  1185.         pop     esi ecx
  1186.         pop     edx
  1187.         pop     eax
  1188.         ret
  1189. .notread:
  1190.         push    ecx
  1191.         mov     dl, 42h
  1192.         mov     ecx, 16
  1193.         call    int13_call
  1194.         pop     ecx
  1195.         test    eax, eax
  1196.         jnz     .v86err
  1197.         test    edx, edx
  1198.         jz      .readerr
  1199.         mov     [bios_read_len], edx
  1200.         mov     edx, [hdpos]
  1201.         mov     [bios_hdpos], edx
  1202.         pop     edx
  1203.         pop     eax
  1204.         mov     [bios_cur_sector], eax
  1205.         jmp     bd_read
  1206. .readerr:
  1207. .v86err:
  1208.         mov     [hd_error], 1
  1209.         jmp     hd_read_error
  1210. ;-----------------------------------------------------------------------------
  1211. align 4
  1212. bd_write_cache_chain:
  1213.         pusha
  1214.         mov     edi, OS_BASE + 0x9A000
  1215.         movzx   ecx, [cache_chain_size]
  1216.         push    ecx
  1217.         shl     ecx, 9-2
  1218.         rep movsd
  1219.         pop     ecx
  1220.         mov     dl, 43h
  1221.         mov     eax, [cache_chain_ptr]
  1222.         mov     eax, [eax]
  1223.         call    int13_call
  1224.         test    eax, eax
  1225.         jnz     .v86err
  1226.         cmp     edx, ecx
  1227.         jnz     .writeerr
  1228.         popa
  1229.         ret
  1230. .v86err:
  1231. .writeerr:
  1232.         popa
  1233.         mov     [hd_error], 1
  1234.         jmp     hd_write_error
  1235. ;-----------------------------------------------------------------------------
  1236. uglobal
  1237. int13_regs_in   rb sizeof.v86_regs
  1238. int13_regs_out  rb sizeof.v86_regs
  1239. endg
  1240. ;-----------------------------------------------------------------------------
  1241. align 4
  1242. int13_call:
  1243. ; Because this code uses fixed addresses,
  1244. ; it can not be run simultaniously by many threads.
  1245. ; In current implementation it is protected by common mutex 'ide_status'
  1246.         mov     word [OS_BASE + 510h], 10h             ; packet length
  1247.         mov     word [OS_BASE + 512h], cx              ; number of sectors
  1248.         mov     dword [OS_BASE + 514h], 9A000000h      ; buffer 9A00:0000
  1249.         mov     dword [OS_BASE + 518h], eax
  1250.         and     dword [OS_BASE + 51Ch], 0
  1251.         push    ebx ecx esi edi
  1252.         mov     ebx, int13_regs_in
  1253.         mov     edi, ebx
  1254.         mov     ecx, sizeof.v86_regs/4
  1255.         xor     eax, eax
  1256.         rep stosd
  1257.         mov     byte [ebx+v86_regs.eax+1], dl
  1258.         mov     eax, [hdpos]
  1259.         lea     eax, [BiosDisksData+(eax-80h)*4]
  1260.         mov     dl, [eax]
  1261.         mov     byte [ebx+v86_regs.edx], dl
  1262.         movzx   edx, byte [eax+1]
  1263. ;        mov     dl, 5
  1264.         test    edx, edx
  1265.         jnz     .hasirq
  1266.         dec     edx
  1267.         jmp     @f
  1268. .hasirq:
  1269.         pushad
  1270.         stdcall enable_irq, edx
  1271.         popad
  1272. @@:
  1273.         mov     word [ebx+v86_regs.esi], 510h
  1274.         mov     word [ebx+v86_regs.ss], 9000h
  1275.         mov     word [ebx+v86_regs.esp], 0A000h
  1276.         mov     word [ebx+v86_regs.eip], 500h
  1277.         mov     [ebx+v86_regs.eflags], 20200h
  1278.         mov     esi, [sys_v86_machine]
  1279.         mov     ecx, 0x502
  1280.         push    fs
  1281.         call    v86_start
  1282.         pop     fs
  1283.         and     [bios_hdpos], 0
  1284.         pop     edi esi ecx ebx
  1285.         movzx   edx, byte [OS_BASE + 512h]
  1286.         test    byte [int13_regs_out+v86_regs.eflags], 1
  1287.         jnz     @f
  1288.         mov     edx, ecx
  1289. @@:
  1290.         ret
  1291. ; \end{diamond}
  1292.