у меня на асме написан код, который отвечает за декодирование ULA. Asm свой, но смысл понять можно.

это - код макросов для эмуляции задержек и ULA.

Код:
////////////////////////////////////////////////////////////////////////////////

          //         //   //                  ////
          //         //   //                 // //
          //         //   //                //  //
          //         //   //               //   //
          //         //   //              ////////
          //         //   //             //     //
            /////////     ////////////  //      //

////////////////////////////////////////////////////////////////////////////////

MC_VIDEO_SCREEN_READY MACRO
    // буфер заполнен, вывод экрана и другие действия
    MOV  EAX, [VideoOutObj]

    // вывод экрана
    LEAVE_MMX
    PUSH ECX
      PUSH EAX
        XOR  B[&ZX].Reg_F, flag_N
        XCHG D[MC_Screen_Buffer_Pos], EDI
        .IF AntiSlowDown and 0
          MOV EDI, 71690+256
        .ENDIF
        MOV  D[&ZX].TactCount, EDI
        SUB  D[&ZX].TactCount, $100
        MOV  [&ZX].Reg_PC, BX
        CALL VideoOutObj_FrameReady
        XCHG [MC_Screen_Buffer_Pos], EDI
        .IF Gfx256
          MOV D[GfxVideoTarget], VideoOutObj_GfxPixels
          MOV D[ScreenAttrTarget], VideoOutObj_ScreenAttrs
        .ENDIF
      POP  EAX
      MOV  DL, 1 // ManageFlash = TRUE
      CALL VideoOutObj_ScreenReady
    POP  ECX
    ENTER_MMX
    XOR  EDI, EDI
  END //MC_VIDEO_SCREEN_READY

//******************************************************************************
ULA_unit MACRO Flag=YES
  .IF MultiColor

    .IF AntiSlowDown
      CMP D[&ZX].AntiSlow_HaltDetected, 0
      JZ  @@ULA_antislow1_&&
      CMP D[&ZX].AntiSlow_HaltDetected, 8
      JB  @@ULA_antislow_end&&
      // halt detected, ...
@@ULA_antislow1_&&: // нет halt'ов, только in-ы
      CMP EDI, 71680
      JAE LONG @@ULA_end_all&&
      XCHG EDI, [MC_Screen_Buffer_Pos]
      CMP  EDI, MC_Screen_Buffer_Size
      XCHG EDI, [MC_Screen_Buffer_Pos]
      JAE LONG @@ULA_end_all&&
@@ULA_antislow_end&&:
    .ENDIF

  .IF PrepareVideo
    LEA  ECX, [EDI-256]
    .IF UlaBuffer > 0
      ADD  ECX, UlaBuffer
    .ENDIF
    SUB  ECX, [ULA_TCounter0]
    JLE  LONG @@_ULA_end&&
    SHR  ECX, 2
    JZ   LONG @@_ULA_end&&

    MOV  EDX, ECX
    SHL  EDX, 2
    ADD  [ULA_TCounter0], EDX

    // CL = число байтов для отображения > 0
    XCHG EDI, [MC_Screen_Buffer_Pos]
    .IF "&Flag" = "YES"
      MOV  [&ZX].Reg_F, AH // если будет вызван FrameReady,
                           // то нужно текущее состояние Reg_F для TimeStamper-а
    .ENDIF
    PUSH EAX  //-------------------------------------\

    // цикл вывода байтов
@@_ULAout_loop&&:

    .IF AntiSlowDown
      CMP EDI, MC_Screen_Buffer_Size
      JAE LONG @@_ULAfin&&
    .ENDIF
    // первым сохраняем текущий BorderColor и видеорежим
    MOV  DL, [&ZX].BorderColor
    MOV  DH, [&ZX].VideoMode
    MOV  [EDI*4+2+MC_Screen_Buffer], DX

    .IF Gfx256 = 0 OR GfxDraw = 0
      .IF GigaScreen
        MOV  [EDI*4+2+MC_Screen_Buffer_Giga], DL
        //int 3
      .ENDIF
    .ENDIF

    // продолжаем цепочку бордюра
    MOV  DX, [&ZX].BorderColorNext
    MOV  [&ZX].BorderColor, DX
    AND  B[&ZX].BorderColorNxt2, $F


    .IF Gfx256 AND GfxDraw  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      // в режиме мультиколора с включенным Gfx надо
      // пересылать Gfx-данные, которых в 8 раз больше
      MOVSX EAX, W[EDI*2+MC_Screen_Table_Pixels]
      //ADD EAX, EAX
      SHL EAX, 3
      JS  SHORT @@_ULA_8pixels_stored&&
      ADD EAX, [GFXVidAddress] // откуда брать 8 байтов
      XCHG ESI, EAX
        XCHG EDI, [GfxVideoTarget]
        MOVSD
        MOVSD
        XCHG EDI, [GfxVideoTarget]
      XCHG ESI, EAX
      // теперь нужен еще атрибут, хотя и Gfx-режим
      // берем байт атрибутов
      MOVZX EAX, W[EDI*2+MC_Screen_Table_Attrs]
      DEC  EAX
      JZ   LONG @@_ULA_8pixels_stored&&

      ADD  EAX, [&ZX].VideoBaseAddr
      MOVZX EDX, B[EAX]
      MOV  DH, [&ZX].Flash
      MOV  AL, B[EDX+GfxAttrConvertTable]

      XCHG EDI, [ScreenAttrTarget]
      STOSB
      XCHG EDI, [ScreenAttrTarget]

    .ELSE Gfx256 = 0 OR GfxDraw = 0   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      // берем байт атрибутов
      MOVZX EAX, W[EDI*2+MC_Screen_Table_Attrs]
      DEC  EAX
      JZ   LONG @@_ULA_8pixels_stored&&

      // проверяем эффект "снег"
      CMP  B[&ZX].SnowEffect, 0
      JZ   SHORT @@_ULAload_data&&

      MOV  AL, B[&ZX].TactRCount
      AND  AL, $7F
      OR   AL, [&ZX].Reg_R_7

      ADD  EAX, [&ZX].VideoBaseAddr
      MOVZX EDX, B[EAX]
      MOV  DH, [&ZX].Flash
      MOV  DL, B[EDX+AttrConvertTable]

      // берем очередной байт 8 пикселов
      MOVZX EAX, W[EDI*2+MC_Screen_Table_Pixels]

      MOV  AL, B[&ZX].TactRCount
      AND  AL, $7F
      OR   AL, [&ZX].Reg_R_7

      ADD  EAX, [VideoBaseAddr]
      MOV  DH, B[EAX]

      MOV  W[EDI*4+MC_Screen_Buffer], DX

      JMP  SHORT @@_ULA_8pixels_stored&&

@@_ULAload_data&&:

      ADD  EAX, [&ZX].VideoBaseAddr
      MOVZX EDX, B[EAX]
      MOV  DH, [&ZX].Flash
      MOV  DL, B[EDX+AttrConvertTable]

      // берем очередной байт 8 пикселов
      MOVZX EAX, W[EDI*2+MC_Screen_Table_Pixels]
      ADD  EAX, [&ZX].VideoBaseAddr
      MOV  DH, B[EAX]

      MOV  W[EDI*4+MC_Screen_Buffer], DX

      .IF GigaScreen // для GigaScreen, то же самое для альтернативного экрана:
          MOVZX EAX, W[EDI*2+MC_Screen_Table_Attrs]
          DEC  EAX
          JZ   SHORT @@_ULA_8pixels_stored&&

          ADD  EAX, [&ZX].AltVideoBaseAddr
          MOVZX EDX, B[EAX]
          MOV  DH, [&ZX].Flash
          MOV  DL, B[EDX+AttrConvertTable]
          // берем очередной байт 8 пикселов
          MOVZX EAX, W[EDI*2+MC_Screen_Table_Pixels]
          ADD  EAX, [&ZX].AltVideoBaseAddr
          MOV  DH, B[EAX]

          MOV  W[EDI*4+MC_Screen_Buffer_Giga], DX
      .ENDIF //GigaScreen

    .ENDIF //not Gfx256 ~~~~~~~~~~~~~~~~~~~~~~~~~

@@_ULA_8pixels_stored&&:
    INC  EDI
    .IF RZX_play = 0
      CMP  EDI, BytesInFrame
      JB   LONG @@_ULAnext&&

      .IF AntiSlowDown
//        CMP BX, $4000
//        JAE @@ULA_AntiSlow_ScreenReady&&
//        MC_VIDEO_SCREEN_READY
//@@ULA_AntiSlow_ScreenReady&&:
      .ELSE
        MC_VIDEO_SCREEN_READY
      .ENDIF
@@ULA_skipscreen&&:

      CMP  B[&ZX].StopOnEndOfFrame, 0
      JZ   SHORT @@_ULAnext&&
      MOV  D[&ZX].JumpPt, StopExec
    .ELSE
      CMP  EDI, BytesInFrame
      JB   SHORT @@_ULAnext&&

      DEC  EDI
    .ENDIF RZX_play = 0

@@_ULAnext&&:
    DEC  ECX
    JG   @@_ULAout_loop&&
    //LOOP @@_ULAout_loop&&

@@_ULAfin&&:
    POP  EAX

    XCHG EDI, [MC_Screen_Buffer_Pos] // EDI = TactCounter+256

@@_ULA_end&&:
  .ELSE //not PrepareVideo
    LEA  ECX, [EDI-256]
    .IF UlaBuffer > 0
      ADD  ECX, UlaBuffer
    .ENDIF

    SUB  ECX, [ULA_TCounter0]
    JLE  LONG @@_ULAnovideo_end&&
    SHR  ECX, 2
    JZ   LONG @@_ULAnovideo_end&&

    MOV  EDX, ECX
    SHL  EDX, 2
    ADD  [ULA_TCounter0], EDX

    // CL = число байтов для "отображения" > 0
    XCHG EDI, [MC_Screen_Buffer_Pos]
    .IF "&Flag" = "YES"
      MOV  [&ZX].Reg_F, AH // если будет вызван FrameReady,
                           // то нужно текущее состояние Reg_F для TimeStamper-а
    .ENDIF
    PUSH EAX  //-------------------------------------\

    // цикл вывода байтов
@@_ULAnovideo_out_loop&&:

    INC  EDI
    .IF RZX_play = 0
      CMP  EDI, BytesInFrame
      JB   SHORT @@_ULAnovideo_next&&

      .IF AntiSlowDown
//        CMP EBX, $4000
//        JAE @@ULA_AntiSlow_ScreenReady1_&&
//        MC_VIDEO_SCREEN_READY
//@@ULA_AntiSlow_ScreenReady1_&&:
      .ELSE
        MC_VIDEO_SCREEN_READY
      .ENDIF

      CMP  B[&ZX].StopOnEndOfFrame, 0
      JZ   SHORT @@_ULAnovideo_next&&
      MOV  D[&ZX].JumpPt, StopExec
    .ELSE
      CMP  EDI, BytesInFrame
      JB   SHORT @@_ULAnovideo_next&&

      DEC  EDI
    .ENDIF RZX_play = 0

@@_ULAnovideo_next&&:
    DEC  ECX
    JG   @@_ULAnovideo_out_loop&&
    //LOOP @@_ULAnovideo_out_loop&&

    POP  EAX

    XCHG EDI, [MC_Screen_Buffer_Pos] // EDI = TactCounter+256

@@_ULAnovideo_end&&:
  .ENDIF //PrepareVideo
@@ULA_end_all&&:
  .ENDIF //MultiColor
  END  //ULA_unit
продолжение следует...