;регистры AY
AY.TonA         = 0
AY.TonB         = 2
AY.TonC         = 4
AY.Noise        = 6
AY.Mixer        = 7
AY.AmplitudeA   = 8.
AY.AmplitudeB   = 9.
AY.AmplitudeC   = 10.
AY.Envelope     = 11.
AY.EnvelopeType = 13.


;ChannelsVars
;--reset group
CHP.Position_In_Ornament        = 0     ;byte позиция в орнаменте
CHP.Position_In_Sample          = 1     ;byte позиция в сэмпле
CHP.Current_Amplitude_Sliding   = 2     ;byte сдвиг амплитуды
CHP.Current_Noise_Sliding       = 3     ;byte сдвиг шума
CHP.Current_Envelope_Sliding    = 4     ;word сдвиг огибающей
CHP.Current_Ton_Sliding         = 6     ;word сдвиг тона
CHP.Ton_Accumulator             = 8.    ;word аккумулятор тона
CHP.Ton_Slide_Count             = 10.   ;byte
CHP.Current_OnOff               = 11.   ;byte
;--reset group
CHP.OnOff_Delay                 = 12.   ;byte
CHP.OffOn_Delay                 = 13.   ;byte
CHP.Envelope_Enabled            = 14.   ;byte
CHP.SimpleGliss                 = 15.   ;byte
CHP.Enabled                     = 16.   ;byte
                            ;   = 17.   ;byte свободно
CHP.Address_In_Pattern          = 18.   ;word адрес внутри текущего раздела
CHP.OrnamentPointer             = 20.   ;word адрес текущего орнамента
CHP.SamplePointer               = 22.   ;word адрес текущего сэмпла
CHP.Slide_To_Note               = 24.   ;word
CHP.Note                        = 26.   ;word
CHP.Ton_Slide_Step              = 28.   ;word
CHP.Ton_Delta                   = 30.   ;word
CHP.Number_Of_Notes_To_Skip     = 32.   ;byte
CHP.Ton_Slide_Delay             = 33.   ;byte
CHP.Note_Skip_Counter           = 34.   ;byte Note_Skip_Counter + Volume должны быть выровнены по слову
CHP.Volume                      = 35.   ;byte
CHP.Tone_Reg                    = 36.   ;byte Tone_Reg + Ampl_Reg должны быть выровнены по слову
CHP.Ampl_Reg                    = 37.   ;byte
CHP = 38.


E36BP=320

;загрузка модуля.
Load:           mov     #1000, SP
                mtps    SP
                mov     #E36BP, R1
                mov     R1, R0
                mov     #3, (R0)+
                mov     #MDLADDR, (R0)+
                clr     (R0)+
                mov     #10, R2
1$:             mov     #20040, (R0)+
                sob     R2, 1$
                emt     36
                tstb    1(R1)
                beq     2$
                halt

DESCR:          .asciz  "ProTracker 3."
                .even
2$:             ;загрузили нормально, теперь убедимся, что это PT3
                mov     #MDLADDR, R1
                mov     #DESCR, R2
4$:             movb    (R2)+, R0
                beq     5$
                movb    (R1)+, R3
                cmpb    R0, #100
                blo     3$
                bic     #240, R0
                bic     #240, R3
3$:             cmpb    R0, R3
                beq     4$
                halt

5$:             call    Start_Play
                jmp     Load

; это для воспроизведения в основном цикле
Start_Play:     mov     #42400, @#177662    ; запрещаем прерывания по вектору 100
                mov     SP, MSP
                mov     @#4, MV4            ; прерывание по вектору 4
                mov     #NV4, @#4           ; поменяем на своё, чтобы по кнопке СТОП прекращать играть
                mov     #102, @#100         ; меняем вектор
                mov     #2, @#102
                mov     #MDLADDR, R1
                call    PT3_Init
                mov     #2400, @#177662
1$:             call    PT3_Play            ; играем квант
                wait                        ; ждём прерывания от вектора 100
                bit     #100, @#177716      ; пока клавиша не нажата
                bne     1$                  ; продолжаем играть
                ; нажатие любой клавиши - прекращение игры
NV4:            mov     #42400, @#177662    ; запрещаем прерывания по вектору 100
                call    MUTE                ; глушим AY
                mov     MV4, @#4            ; всё восстанавливаем назад
                mov     MSP, SP
                return

; это для воспроизведения в фоне
;Start_Play:     mov     #42400, @#177662    ; запрещаем прерывания по вектору 100
;                mov     @#4, MV4            ; прерывание по вектору 4
;                mov     #NV4, @#4           ; поменяем на своё, чтобы по кнопке СТОП прекращать играть
;                mov     #TRPT, @#100        ; адрес фоновой процедуры воспроизведения
;                mov     #340, @#102         ; адрес вектора
;                mov     #MDLADDR, R1
;                call    PT3_Init            ; инициализация переменных модуля воспроизведения
;                mov     #2400, @#177662     ; разрешаем прерывания обратно
;                return                      ; и выход, правда там уже надо не на загрузку следующего модуля
;                                            ; выходить, а куда-нибудь, куда надо
;
;NV4:            mov     #42400, @#177662    ; запрещаем прерывания по вектору 100
;                call    MUTE                ; глушим AY
;                mov     MV4, @#4            ; всё восстанавливаем назад
;                halt                        ; и делаем прерывание по вектору 4
;                                            ; или какие-то свои действия

MSP:            .word   0
MV4:            .word   0

TRPT:           mov     R0, -(SP)
                mov     R1, -(SP)
                mov     R2, -(SP)
                mov     R3, -(SP)
                mov     R4, -(SP)
                mov     R5, -(SP)
                call    PT3_Play
                mov     (SP)+, R5
                mov     (SP)+, R4
                mov     (SP)+, R3
                mov     (SP)+, R2
                mov     (SP)+, R1
                mov     (SP)+, R0
                rti

PT3_Play:       clr     AddToEnvelope
                clrb    AYREGS + AY.Mixer
                movb    #-1, AYREGS + AY.EnvelopeType
                decb    DelayCounter    ;уменьшаем текущий счётчик темпа
                bgt     0PL2            ;пока не 0, играть текущую ноту
                mov     PC, R5
                add     #Channel_A-., R5
                decb    CHP.Note_Skip_Counter(R5)
                bne     0PL1B
                mov     CHP.Address_In_Pattern(R5), R3  ;текущий адрес в текущем разделе в канале A
                tstb    (R3)            ;раздел закончился?
                bne     0PL1A           ;нет ещё
                clrb    Noise_Base
                mov     CurrentPosition, R0 ;текущая позиция в ордере
                inc     R0
                movb    (R0), R1            ;номер раздела, умноженный на 3
                cmpb    #377, R1            ;ещё не кончились? (377 - конец ордера)
                bne     0PLNLP          ;нет
                call    CHECKLP         ;да - проверим, нужно ли зацикливание
                mov     LoopPosition, R0    ;если да - начнём с начала
                movb    (R0), R1
0PLNLP:         mov     R0, CurrentPosition     ;сохраним текущую позицию
                bic     #177400, R1
                asl     R1
                add     PT3_PatternsPointer, R1 ;получаем адрес нужного раздела
                mov     Module_Address, R2

                clr     R3
                bisb    (R1)+, R3       ;формируем адреса каналов
                swab    R3
                bisb    (R1)+, R3       ;беда в том, что слова обычно не располагаются по чётным адресам,
                swab    R3              ;т.е. слово по чётному адресу - очень редкое явление
                add     R2, R3

                clr     R0
                bisb    (R1)+, R0
                swab    R0
                bisb    (R1)+, R0
                swab    R0
                add     R2, R0
                mov     R0, Channel_B + CHP.Address_In_Pattern

                clr     R0
                bisb    (R1)+, R0
                swab    R0
                bisb    (R1)+, R0
                swab    R0
                add     R2, R0
                mov     R0, Channel_C + CHP.Address_In_Pattern

0PL1A:          call    PT3_PatternInterpreter
                mov     R3, CHP.Address_In_Pattern(R5)

0PL1B:          add     #CHP, R5
                decb    CHP.Note_Skip_Counter(R5)
                bne     0PL1C
                mov     CHP.Address_In_Pattern(R5), R3
                call    PT3_PatternInterpreter
                mov     R3, CHP.Address_In_Pattern(R5)

0PL1C:          add     #CHP, R5
                decb    CHP.Note_Skip_Counter(R5)
                bne     0PL1D
                mov     CHP.Address_In_Pattern(R5), R3
                call    PT3_PatternInterpreter
                mov     R3, CHP.Address_In_Pattern(R5)

0PL1D:          movb    Delay, DelayCounter

0PL2:           mov     PC, R5
                add     #Channel_A-., R5
                call    PT3_ChangeRegisters
                call    PT3_ChangeRegisters
                call    PT3_ChangeRegisters

                movb    Noise_Base, R0  ;0..1F
                add     AddToNoise, R0
                movb    R0, AYREGS + AY.Noise

                mov     Envelope_Base, R0
                add     AddToEnvelope, R0
                add     Cur_Env_Slide, R0
                movb    R0, AYREGS + AY.Envelope
                swab    R0
                movb    R0, AYREGS + AY.Envelope + 1

                movb    Cur_Env_Delay, R0
                beq     1$
                decb    R0
                bne     2$
                movb    Env_Delay, R0
                add     Env_Slide_Add, Cur_Env_Slide
2$:             movb    R0, Cur_Env_Delay
1$:

PT3_ROut:       mov     #177714, R5
                mov     #362, R1
                mov     PC, R4
                mov     #AYREGS + AY.EnvelopeType-., R4
                movb    (R4), R0
                bmi     1$
                mov     R1, (R5)
                comb    R0
                movb    R0, (R5)
1$:             inc     R1
2$:             mov     R1, (R5)
                movb    -(R4), R0
                comb    R0
                movb    R0, (R5)
                incb    R1
                bmi     2$
                return

;pattern decoder
PD_OrSm:        clrb    CHP.Envelope_Enabled(R5)
                call    SetOrnament
                movb    (R3)+, R0
                br      PD_SAM_

PD_SAM:         sub     #0xD0, R0
                asl     R0
PD_SAM_:        add     PT3_SamplesPointers, R0
                clr     R1
                bisb    (R0)+, R1
                swab    R1
                bisb    (R0), R1
                swab    R1
                tst     R1
                beq     3$  ;чё то, на неиспользуемый сэмпл попали, проигнорируем
                add     Module_Address, R1
                mov     R1, CHP.SamplePointer(R5)
3$:             br      PD_LOOP

PD_VOL:         bic     #0xFFF0, R0
                asl     R0
                asl     R0
                asl     R0
                asl     R0
                movb    R0, CHP.Volume(R5)
                br      PD_LOOP

PD_EOff:        clrb    CHP.Envelope_Enabled(R5)
                clrb    CHP.Position_In_Ornament(R5)
                br      PD_LOOP

PD_SorE:        bic     #0xFFF0, R0
                decb    R0
                bne     PD_ENV
                movb    (R3)+, CHP.Number_Of_Notes_To_Skip(R5)
                br      PD_LOOP

PD_ENV:         call    SetEnvelope
                clrb    CHP.Position_In_Ornament(R5)
                br      PD_LOOP

PD_ORN:         call    SetOrnament
                br      PD_LOOP

PD_ESAM:        clrb    CHP.Envelope_Enabled(R5)
                bic     #0xFFF0, R0
                beq     1$
                call    SetEnvelope
1$:             clrb    CHP.Position_In_Ornament(R5)
                movb    (R3)+, R0
                br      PD_SAM_

;вход:  R3 - адрес данных в канале
;       R5 - адрес блока параметров канала
PT3_PatternInterpreter:
                mov     CHP.Note(R5), PrNote
                mov     CHP.Current_Ton_Sliding(R5), PrSliding

PD_LOOP:        clr     R0
                bisb    (R3)+, R0
                cmpb    R0, #0xF0
                bhis    PD_OrSm         ;F0..FF - задать номер орнамента (0..F) и номер сэмпла
                cmpb    R0, #0xD0
                beq     PD_FIN          ;D0 - ЗАКОНЧИТЬ анализ строки.
                bhi     PD_SAM          ;D1..EF - задать номер сэмпла.
                cmpb    R0, #0xC0
                beq     PD_REL          ;C0..CF - задать громкость.(v=0 - пауза и ЗАКОНЧИТЬ анализ строки.)
                bhi     PD_VOL
                cmpb    R0, #0xB0
                beq     PD_EOff         ;B0 - выключить Envelope
                bhi     PD_SorE         ;B1, lines - не анализировать канал в течение lines строк
                                        ;B2..BF - то же, что 1x, но без сэмпла и орнамент x-1
                cmpb    R0, #0x50
                bhis    PD_NOTE         ;50..AF - задать высоту ноты и ЗАКОНЧИТЬ анализ текущей строки канала.
                cmpb    R0, #0x40
                bhis    PD_ORN          ;40..4F - задать орнамент номер x. (огибающая не выключается)
                cmpb    R0, #0x20
                bhis    PD_NOIS         ;20..3F - задать смещение шума (бывает только в канале B)
                cmpb    R0, #0x10
                bhis    PD_ESAM         ;10 - выключить огибающую, перезапустить орнамент и изменить номер сэмпла.
                                        ;11..1F - изменить номер сэмпла, перезапустить орнамент и включить огибающую типа x с периодом
                ;остались коды 00..0F
                asl     R0
                mov     SPCCOMS(R0), -(SP)  ;действие в стек, потом они все пакетно обработаются
                br      PD_LOOP

PD_NOIS:        bic     #0xFFE0, R0
                movb    R0, Noise_Base
                br      PD_LOOP

PD_REL:         clrb    CHP.Enabled(R5)
                br      PD_RES

PD_NOTE:        sub     #0x50, R0
                mov     R0, CHP.Note(R5)
                movb    #-1, CHP.Enabled(R5)

PD_RES:         mov     R5, R0
                clr     (R0)+   ;0
                clr     (R0)+   ;2
                clr     (R0)+   ;4
                clr     (R0)+   ;6
                clr     (R0)+   ;8
                clr     (R0)    ;10

PD_FIN:         movb    CHP.Number_Of_Notes_To_Skip(R5), CHP.Note_Skip_Counter(R5)
                return

C_PORTM:        clrb    CHP.SimpleGliss(R5)
                movb    (R3)+, R0
;SKIP PRECALCULATED TONE DELTA (BECAUSE
;CANNOT BE RIGHT AFTER PT3 COMPILATION)
                inc     R3
                inc     R3
                movb    R0, CHP.Ton_Slide_Delay(R5)
                movb    R0, CHP.Ton_Slide_Count(R5)
                mov     CHP.Note(R5), R2
                mov     R2, CHP.Slide_To_Note(R5)
                asl     R2
                mov     NT_(R2), R2
                mov     PrNote, R1
                mov     R1, CHP.Note(R5)
                asl     R1
                mov     NT_(R1), R1
                sub     R1, R2
                mov     R2, CHP.Ton_Delta(R5)   ;Ton_Delta = GetNoteFreq(Note) - GetNoteFreq(PrNote);
                mov     CHP.Current_Ton_Sliding(R5), R1
                cmpb    Version, #6
                blo     OLDPRTM         ;Old 3xxx for PT v3.5-
                mov     PrSliding, R1
                mov     R1, CHP.Current_Ton_Sliding(R5)
OLDPRTM:        clr     R0
                bisb    (R3)+, R0       ;SIGNED TONE STEP мл.байт
                swab    R0
                bisb    (R3)+, R0       ;ст.байт
                swab    R0
                tst     R0              ;Ton_Slide_Step := Abs(SmallInt(PWord(@Index[Address_In_Pattern])^));
                bpl     1$
                neg     R0
1$:             sub     R1, R2          ;if (Ton_Delta - Current_Ton_Sliding < 0)
                bpl     2$              ;         Ton_Slide_Step = -Ton_Slide_Step
                neg     R0
2$:             mov     R0, CHP.Ton_Slide_Step(R5)
                clrb    CHP.Current_OnOff(R5)
                return

C_GLISS:        movb    (PC), CHP.SimpleGliss(R5)
                movb    (R3)+, R0
                movb    R0, CHP.Ton_Slide_Delay(R5)
                bne     GL36
                ;а если 0, то от версии зависит
                cmpb    Version, #7     ;AlCo PT3.7+
                blo     GL36
                incb    R0  ;если < 7 то тут 0, если >= 7 то 1
GL36:           movb    R0, CHP.Ton_Slide_Count(R5)
                movb    (R3)+, CHP.Ton_Slide_Step(R5) ;мл
                movb    (R3)+, CHP.Ton_Slide_Step + 1(R5) ;ст
                clrb    CHP.Current_OnOff(R5)
                return

C_SMPOS:        movb    (R3)+, CHP.Position_In_Sample(R5)
                return

C_ORPOS:        movb    (R3)+, CHP.Position_In_Ornament(R5)
                return

C_VIBRT:        movb    (R3), CHP.OnOff_Delay(R5)
                movb    (R3)+, CHP.Current_OnOff(R5)
                movb    (R3)+, CHP.OffOn_Delay(R5)
                clrb    CHP.Ton_Slide_Count(R5)
                clr     CHP.Current_Ton_Sliding(R5)
                return

C_ENGLS:        movb    (R3), Env_Delay
                movb    (R3)+, Cur_Env_Delay
                movb    (R3)+, Env_Slide_Add
                movb    (R3)+, Env_Slide_Add + 1
                return

C_DELAY:        movb    (R3)+, Delay
                return

;вход:  R0 - номер огибающей
SetEnvelope:    movb    #0x10, CHP.Envelope_Enabled(R5)
                movb    R0, AYREGS + AY.EnvelopeType
                movb    (R3)+, Envelope_Base + 1    ;сперва старший байт
                movb    (R3)+, Envelope_Base        ;потом младший
                clrb    Cur_Env_Delay
                clr     Cur_Env_Slide
C_NOP:          return

;вход:  R0 - код орнамента, в нём 4 мл.бита - номер
SetOrnament:    bic     #0xFFF0, R0
                asl     R0
                add     PT3_OrnamentsPointers, R0
                clr     R1
                bisb    (R0)+, R1
                swab    R1
                bisb    (R0), R1
                swab    R1
                tst     R1
                bne     1$  ;если задали реальный орнамент
                mov     PC, R1
                add     #EMPTYSAMORN-., R1
                br      2$
1$:             add     Module_Address, R1
2$:             mov     R1, CHP.OrnamentPointer(R5)
                clrb    CHP.Position_In_Ornament(R5)
                return

;ALL 16 ADDRESSES TO PROTECT FROM BROKEN PT3 MODULES
SPCCOMS:        .word   C_NOP
                .word   C_GLISS ;1
                .word   C_PORTM ;2
                .word   C_SMPOS ;3
                .word   C_ORPOS ;4
                .word   C_VIBRT ;5
                .word   C_NOP
                .word   C_NOP
                .word   C_ENGLS ;8
                .word   C_DELAY ;9
                .word   C_NOP
                .word   C_NOP
                .word   C_NOP
                .word   C_NOP
                .word   C_NOP
                .word   C_NOP

;вход:  R5 - адрес блока параметров канала
PT3_ChangeRegisters:
                clr     R1  ;амплитуда
                tstb    CHP.Enabled(R5)
                beq     0ex$
                mov     CHP.OrnamentPointer(R5), R1 ;указатель на орнамент
                movb    (R1)+, R4       ;loop
                movb    (R1)+, R3       ;length
                movb    CHP.Position_In_Ornament(R5), R0    ;предполагается, что длина не может быть больше 127.
                add     R0, R1
                incb    R0
                cmpb    R0, R3
                blo     CH_ORPS
                movb    R4, R0
CH_ORPS:        movb    R0, CHP.Position_In_Ornament(R5)
                mov     CHP.Note(R5), R0    ;не может быть больше 0127.
                movb    (R1), R1    ;смещение ноты из орнамента     ; signed byte->word
                add     R1, R0
                bpl     CH_NTP
                clr     R0
CH_NTP:         cmp     R0, #96.
                blo     CH_NOK
                mov     #95., R0
CH_NOK:         asl     R0
                mov     R0, -(SP)   ;нота * 2
                mov     CHP.SamplePointer(R5), R1   ;указатель на сэмпл
                movb    (R1)+, R4       ;loop
                movb    (R1)+, R3       ;length
                movb    CHP.Position_In_Sample(R5), R0  ;предполагается, что длина не может быть больше 127.
                add     R0, R1
                add     R0, R1
                add     R0, R1
                add     R0, R1
                incb    R0
                cmpb    R0, R3
                blo     CH_SMPS
                movb    R4, R0
CH_SMPS:        movb    R0, CHP.Position_In_Sample(R5)
                movb    (R1)+, R3   ;R3=C
                movb    (R1)+, R4   ;R4=B
                clr     R2
                bisb    (R1)+, R2   ;смещение периода тона в сэмпле
                swab    R2
                bisb    (R1), R2
                swab    R2
                add     CHP.Ton_Accumulator(R5), R2
                bitb    #0x40, R4   ;смещение тона запоминать?
                beq     CH_NOAC
                mov     R2, CHP.Ton_Accumulator(R5)
CH_NOAC:        mov     (SP)+, R1   ;достаём ноту *2
                add     NT_(R1), R2 ;прибавляем тон ноты к смещению
                mov     CHP.Current_Ton_Sliding(R5), R1 ;текущий сдвиг тона
                add     R1, R2      ;и его прибавляем
                bic     #0xF000, R2
                movb    CHP.Tone_Reg(R5), R0
                mov     R2, AYREGS(R0)      ;результат сохраним

                tstb    CHP.Ton_Slide_Count(R5)
                beq     CH_AMP
                decb    CHP.Ton_Slide_Count(R5)
                bne     CH_AMP
                movb    CHP.Ton_Slide_Delay(R5), CHP.Ton_Slide_Count(R5)
                mov     CHP.Ton_Slide_Step(R5), R2
                add     R2, R1  ;Current_Ton_Sliding += Ton_Slide_Step
                mov     R1, CHP.Current_Ton_Sliding(R5)
                tstb    CHP.SimpleGliss(R5) ;какой эффект?
                bne     CH_AMP
                mov     CHP.Ton_Delta(R5), R0
                tst     R2          ;if ((Ton_Slide_Step < 0) and (Current_Ton_Sliding <= Ton_Delta)) or
                                    ;   ((Ton_Slide_Step >= 0) and (Current_Ton_Sliding >= Ton_Delta)) then
                bpl     1$
                cmp     R1, R0
                ble     2$
                br      CH_AMP

0ex$:           br      CH_EXIT

1$:             cmp     R1, R0
                blt     CH_AMP
2$:             mov     CHP.Slide_To_Note(R5), CHP.Note(R5)
                clrb    CHP.Ton_Slide_Count(R5)
                clr     CHP.Current_Ton_Sliding(R5)

CH_AMP:         movb    CHP.Current_Amplitude_Sliding(R5), R0   ; signed byte->word
                bitb    #0x80, R3       ;признак съезжания громкости
                beq     CH_NOAM
                bitb    #0x40, R3       ;увеличивать?
                beq     CH_AMIN
                cmpb    R0, #15.            ;уже и так максимум?
                beq     CH_NOAM
                incb    R0              ;увеличиваем
                br      CH_SVAM
                ;уменьшаем
CH_AMIN:        cmpb    R0, #-15.       ;уже и так минимум?
                beq     CH_NOAM
                decb    R0              ;уменьшаем
CH_SVAM:        movb    R0, CHP.Current_Amplitude_Sliding(R5)
CH_NOAM:        mov     R4, R1          ;B
                bic     #0xFFF0, R1     ;оставляем громкость
                add     R1, R0          ;прибавляем сдвиг
                bpl     CH_APOS
                clr     R0
CH_APOS:        cmp     R0, #16.
                blo     CH_VOL
                mov     #15., R0            ;нормализуем
CH_VOL:         bisb    CHP.Volume(R5), R0  ;R0 всегда 0..15, CHP.Volume - всегда старшие 4 бита от байта
                movb    VT_(R0), R1     ;значение громкости из таблицы
                bitb    #1, R3          ;маска огибающей есть?
                bne     CH_NOEN
                bisb    CHP.Envelope_Enabled(R5), R1    ;ставим маску огибающей
CH_NOEN:        ; R1 - всё, готовый результат амплитуда
                ; R1 дальше не использовать
                mov     R3, R0
                asr     R0
                bic     #0xFFE0, R0     ;получаем значение шума или сдвига огибающей
                bitb    #0x80, R4       ;что там шум или огибающая? 1 - огиб, 0 - шум
                beq     NO_ENSL
                bit     #0x10, R0       ;знак у значения есть?
                beq     3$              ;нету
                bis     #0xFFE0, R0     ;есть - распространяем знак на старшие биты
3$:             add     CHP.Current_Envelope_Sliding(R5), R0
                bitb    #0x20, R4       ;огибающую запоминать?
                beq     NO_ENAC
                mov     R0, CHP.Current_Envelope_Sliding(R5)
NO_ENAC:        add     R0, AddToEnvelope
                br      CH_MIX

NO_ENSL:        movb    CHP.Current_Noise_Sliding(R5), R3   ;текущий сдвиг шума signed byte->word
                add     R3, R0
                mov     R0, AddToNoise
                bitb    #0x20, R4       ;шум запоминать?
                beq     CH_MIX
                movb    R0, CHP.Current_Noise_Sliding(R5)

CH_MIX:         asr     R4  ;двигаем вправо
                bic     #177667, R4 ;оставляем биты 6 и 3, там сейчас Nm и Tm
                bisb    R4, AYREGS + AY.Mixer

CH_EXIT:        movb    CHP.Ampl_Reg(R5), R0
                movb    R1, AYREGS(R0)
                asrb    AYREGS + AY.Mixer
                tstb    CHP.Current_OnOff(R5)
                beq     0Ret$
                decb    CHP.Current_OnOff(R5)
                bne     0Ret$
                movb    CHP.OnOff_Delay(R5), R0
                comb    CHP.Enabled(R5)
                bne     CH_ONDL
                movb    CHP.OffOn_Delay(R5), R0
CH_ONDL:        movb    R0, CHP.Current_OnOff(R5)
0Ret$:          add     #CHP, R5
                return

CHECKLP:        bisb    #200, SETUP
                bitb    #1, SETUP
                bne     1$
                return
1$:             tst     (SP)+
                incb    DelayCounter
                incb    Channel_A + CHP.Note_Skip_Counter
MUTE:           clr     AYREGS + AY.AmplitudeA
                clrb    AYREGS + AY.AmplitudeC
                movb    #77, AYREGS + AY.Mixer
                jmp     PT3_ROut


FillPT3:        mov     R0, CHP.Note_Skip_Counter(R4)
                mov     R2, CHP.Address_In_Pattern(R4)  ;ptr to zero
                mov     R2, CHP.OrnamentPointer(R4) ;ornament 0 is "0, 1, 0" in all versions from 3.xx to 3.6x and VTII
                mov     R2, CHP.SamplePointer(R4)   ;There is no default
                mov     R3, CHP.Tone_Reg(R4)
                add     #CHP, R4
                return

;Вход:  R1 - AddressOfModule
PT3_Init:       mov     R1, Module_Address  ;сохраняем адрес начала модуля
                mov     R1, R5      ;запомним
                movb    100.(R5), Delay ;смещение +100 - темп
                add     #200., R1       ;смещение +201
                mov     R1, CurrentPosition ;начало списка позиций (ордер) - 1, потому что там используется ++CurrentPosition
                movb    102.(R5), R2    ;смещение +102 song loop (0=зацикливание на начало).
                add     R2, R1
                inc     R1
                mov     R1, LoopPosition    ;позиция зацикливания
                clr     R1
                bisb    104.(R5), R1 ;ст. байт
                swab    R1
                bisb    103.(R5), R1 ;мл. байт
                add     R5, R1      ;адрес таблицы разделов
                mov     R1, PT3_PatternsPointer ;сохраним
                mov     #169., R1
                add     R5, R1      ;смещение +169 - смещения от начала модуля до орнаментов
                mov     R1, PT3_OrnamentsPointers   ;запомним
                mov     #105., R1
                add     R5, R1      ;смещение +105 - смещения  от начала модуля до сэмплов
                mov     R1, PT3_SamplesPointers ;запомним
                bicb    #200, SETUP

                push    R5
                ;распаковка запакованного массива нот
;(c) Ivan Roshin
                mov     #T_PACK, R4 ;начало массива упакованных нот
                mov     #T1_ + 98., R5  ;конечный адрес распакованных нот
                mov     #4, R3
                mov     #12., R2
3$:             mov     (R4)+, R0   ;берём первое слово
                asl     R0          ;нужно всё умножить на 2 т.к. по алгоритму там всё делится на 2 для каждой октавы, и для первой - тоже
                br      1$          ;и сразу запишем

2$:             clr     R1
                bisb    (R4)+, R1   ;берём очередное смещение
                add     R1, R0      ;прибавляем
                add     R1, R0
1$:             mov     R0, -(R5)   ;записываем ноту
                sob     R2, 2$      ;и так всю октаву
                movb    (R4)+, R2   ;получаем кол-во распаковываемых нот
                inc     R4
                bic     #1, R4      ;even
                sob     R3, 3$      ;и так все 4 таблицы
                pop     R5
                ;инициализация переменных
                mov     #VAR0START, R1
                mov     #VAR0END-VAR0START, R3
4$:             clrb    (R1)+
                sob     R3, 4$

                movb    #1, DelayCounter
                mov     #0xF001, R0     ;ст. - CHP.Volume, мл. - CHP.Note_Skip_Counter
                mov     #EMPTYSAMORN, R2
                mov     #Channel_A, R4
                mov     (PC)+, R3
                .byte   AY.TonA, AY.AmplitudeA
                call    FillPT3
                mov     (PC)+, R3
                .byte   AY.TonB, AY.AmplitudeB
                call    FillPT3
                mov     (PC)+, R3
                .byte   AY.TonC, AY.AmplitudeC
                call    FillPT3

                movb    13.(R5), R0         ;извлекаем номер версии: код символа 7..3 или другой
                sub     #60, R0             ;получим число
                bcs     20$                 ;если было <"0"
                cmpb    R0, #10.
                blo     21$                 ;или было >"9", т.е. вообще не цифра
20$:            mov     #6, R0              ;то версия по умолчанию = 6
21$:            movb    R0, Version         ;сохраним версию
                push    R0                  ;и запомним на будущее
                cmpb    R0, #4              ;сравним с 4
                movb    99.(R5), R0         ;номер частотной таблицы: 0..3
                rolb    R0                  ;*2 (+ 1, если версия < 4)
                bicb    #177770, R0         ;входной параметр для следующей функции

;NoteTableCreator (c) Ivan Roshin
;R0 - NoteTableNumber*2 + VersionForNoteTable
;(xx1b - 3.xx..3.4r, xx0b - 3.4x..3.6x..VTII1.0)
;на входе R1==T1_
NoteTableCreator:
                mov     R1, -(SP)       ;R1 == T1_
                mov     #NT_DATA, R1
                add     R0, R1
                add     R0, R1
                add     R0, R1          ;адрес параметров для нужной таблицы
                clr     R2
                bisb    (R1)+, R2       ;берём первый параметр.
                mov     (PC)+, R0
                    nop
                tstb    (R1)+           ;смотрим признак
                beq     11$
                mov     (PC)+, R0       ;если бит С был, то команда clc
                    clc
11$:            mov     R0, M2

                clr     R3
                bisb    (R1), R3            ;второй параметр
                add     #T_, R3
                add     (SP)+, R2       ;R2 += T1_
;R2 - адрес нужной октавы в распакованной таблице октав
;R3 - адрес нужного набора переменных из массива T_

                push    R3
                mov     #NT_, R1            ;тут будем формировать нотную таблицу
                push    R1

                mov     #12., R4            ;цикл по всем 12 нотам октавы
13$:            mov     (R2)+, R3       ;частота ноты

                push    R1
                mov     #8., R5         ;8 октав, формируем ноту во всех октавах
12$:            clc
                ror     R3
                call    M2              ;temporary
                mov     R3, R0
                adc     R0
                mov     R0, (R1)
                add     #24., R1
                sob     R5, 12$
                pop     R1
                tst     (R1)+           ;переходим к следующей ноте
                sob     R4, 13$

                pop     R2              ;NT_
                pop     R1              ;адрес нужного набора переменных из массива T_

                cmp     R1, #TCOLD_1
                bne     16$
                movb    #0xfd, NT_ + 56
16$:            clr     R0
                bisb    (R1)+, R0
                beq     14$
                clr     R5
                rorb    R0  ;извлекаем мл.бит
                rol     R5  ;помещаем сюда
                aslb    R0  ;а смещение восстанавливаем
                add     R0, R2  ;получаем адрес ноты
                tst     R5  ;мл.бит был?
                beq     15$ ;нет
                dec     (R2)
                dec     (R2)
15$:            inc     (R2)
                sub     R0, R2 ;возвращаем всё обратно
                br      16$

14$:            pop     R0              ;версия, вх. параметр для следующей функции

;VolTableCreator (c) Ivan Roshin
;R0 - VersionForVolumeTable (0..4 - 3.xx..3.4x;
;                           5.. - 3.5x..3.6x..VTII1.0)
VolTableCreator:
                mov     #21, R3 ;инкремент
                clr     R1      ;накопитель инкремента
                cmpb    R0, #5  ;версия какая?
                mov     (PC)+, R0
                    aslb    R0  ;для >= 5 нужно asl
                bhis    1$      ;>= 5
                dec     R3      ;для < 5 корректируем инкремент
                mov     R3, R1  ;и накопитель
                mov     (PC)+, R0
                    nop         ;для < 5 нужно nop
1$:             mov     R0, M2

                mov     #VT_, R4    ;начало таблицы
                mov     R4, R5
                mov     #16., R2    ;первые 16 байтов - нули, там делать нечего
                add     R2, R5
                mov     #256., R0
2$:             clrb    (R4)+   ;таблицу сперва почистим
                sob     R0, 2$

5$:             push    R3      ;инкремент сохраним

                add     R3, R1  ;таинственные действия
                mov     #0, R3  ;чтобы бит С не попортить
                sbc     R3      ;тут будет или 0 или -1 начальное значение

4$:             movb    R3, R0  ;R3 нужно разделить на младший
                mov     R3, R4  ;и старший байты
                clrb    R4
                swab    R4      ;старший байт из R3
                call    M2
                adcb    R4
                movb    R4, (R5)+
                add     R1, R3
                inc     R2      ;считаем
                mov     R2, R0
                bic     #177760, R0 ;пока ряд из 16 байтов не заполним
                bne     4$

                pop     R3
                cmp     R1, #119.
                bne     3$
                inc     R1
3$:             tstb    R2      ;пока не заполним всю таблицу
                bne     5$
                jmp     PT3_ROut

M2:             nop
                return

T_:

TCOLD_0:        .byte 0x00 + 1, 0x04 + 1, 0x08 + 1, 0x0A + 1, 0x0C + 1, 0x0E + 1, 0x12 + 1, 0x14 + 1
                .byte 0x18 + 1, 0x24 + 1, 0x3C + 1, 0
TCNEW_1:
TCOLD_1:        .byte 0x5C + 1, 0
TCOLD_2:        .byte 0x30 + 1, 0x36 + 1, 0x4C + 1, 0x52 + 1, 0x5E + 1, 0x70 + 1, 0x82, 0x8C
                .byte 0x9C, 0x9E, 0xA0, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xAE, 0

TCNEW_3:        .byte 0x56 + 1
TCOLD_3:        .byte 0x1E + 1, 0x22 + 1, 0x24 + 1, 0x28 + 1, 0x2C + 1, 0x2E + 1, 0x32 + 1, 0xBE + 1, 0

TCNEW_0:        .byte 0x1C + 1, 0x20 + 1, 0x22 + 1, 0x26 + 1, 0x2A + 1, 0x2C + 1, 0x30 + 1, 0x54 + 1
                .byte 0xBC + 1, 0xBE + 1, 0

TCNEW_2:        .byte 0x1A + 1, 0x20 + 1, 0x24 + 1, 0x28 + 1, 0x2A + 1, 0x3A + 1, 0x4C + 1, 0x5E + 1
                .byte 0xBA + 1, 0xBC + 1, 0xBE + 1, 0

EMPTYSAMORN: ;нулевой орнамент и сэмпл
                .byte 0, 1, 0, 0x90, 0, 0 ;delete 0x90, 0, 0 if you don't need default sample
                .even
;значения нот первых октав (упакованные)
T_PACK:         .word 0x06EC
                .byte 0x0755-0x06EC
                .byte 0x07C5-0x0755
                .byte 0x083B-0x07C5
                .byte 0x08B8-0x083B
                .byte 0x093D-0x08B8
                .byte 0x09CA-0x093D
                .byte 0x0A5F-0x09CA
                .byte 0x0AFC-0x0A5F
                .byte 0x0BA4-0x0AFC
                .byte 0x0C55-0x0BA4
                .byte 0x0D10-0x0C55
                .byte 13.   ;количество распаковываемых нот
                .even
                .word 0x066D
                .byte 0x06CF-0x066D
                .byte 0x0737-0x06CF
                .byte 0x07A4-0x0737
                .byte 0x0819-0x07A4
                .byte 0x0894-0x0819
                .byte 0x0917-0x0894
                .byte 0x09A1-0x0917
                .byte 0x0A33-0x09A1
                .byte 0x0ACF-0x0A33
                .byte 0x0B73-0x0ACF
                .byte 0x0C22-0x0B73
                .byte 0x0CDA-0x0C22
                .byte 12.   ;количество распаковываемых нот
                .even
                .word 0x0704
                .byte 0x076E-0x0704
                .byte 0x07E0-0x076E
                .byte 0x0858-0x07E0
                .byte 0x08D6-0x0858
                .byte 0x095C-0x08D6
                .byte 0x09EC-0x095C
                .byte 0x0A82-0x09EC
                .byte 0x0B22-0x0A82
                .byte 0x0BCC-0x0B22
                .byte 0x0C80-0x0BCC
                .byte 0x0D3E-0x0C80
                .byte 12.   ;количество распаковываемых нот
                .even
                .word 0x07E0
                .byte 0x0858-0x07E0
                .byte 0x08E0-0x0858
                .byte 0x0960-0x08E0
                .byte 0x09F0-0x0960
                .byte 0x0A88-0x09F0
                .byte 0x0B28-0x0A88
                .byte 0x0BD8-0x0B28
                .byte 0x0C80-0x0BD8
                .byte 0x0D60-0x0C80
                .byte 0x0E10-0x0D60
                .byte 0x0EF8-0x0E10
                .even


SETUP:                  .byte 0 ;set bit0 to 1, if you want to play without looping
                                ;bit7 is set each time, when loop point is passed
Version:                .byte 0
Delay:                  .byte 0 ;темп
Env_Delay:              .byte 0
                        .even
Module_Address:         .word 0 ;начало загруженного модуля
PT3_SamplesPointers:    .word 0 ;начало таблицы смещений до сэмплов
PT3_OrnamentsPointers:  .word 0 ;начало таблицы смещений до орнаментов
PT3_PatternsPointer:    .word 0 ;начало таблицы разделов
LoopPosition:           .word 0 ;начало ордера
CurrentPosition:        .word 0 ;адрес текущей позиции в ордере

VAR0START:  ;Start of Init ZERO area

PrNote:                 .word 0 ;word предыдущая нота
PrSliding:              .word 0 ;word предыдущий сдвиг ноты
AddToEnvelope:          .word 0 ;прибавка к частоте огибающей
Env_Slide_Add:          .word 0
Cur_Env_Slide:          .word 0
AddToNoise:             .word 0
DelayCounter:           .byte 0 ;текущий счётчик темпа
Cur_Env_Delay:          .byte 0
Noise_Base:             .byte 0
                        .even

Channel_A:              .blkb   CHP
Channel_B:              .blkb   CHP
Channel_C:              .blkb   CHP

AYREGS:
VT_:            .blkb   256.    ;CreatedVolumeTableAddress
                                ;а ещё временный буфер для нот.
                                ;а первые 16 байтов - буфер регистров AY
Envelope_Base = AYREGS + 14.

T1_ = VT_ + 16. ;Tone tables data depacked here
T_OLD_1 = T1_
T_OLD_2 = T_OLD_1 + 24.
T_OLD_3 = T_OLD_2 + 24.
T_OLD_0 = T_OLD_3 + 2.

T_NEW_0 = T_OLD_0
T_NEW_1 = T_OLD_1
T_NEW_2 = T_NEW_0 + 24.
T_NEW_3 = T_OLD_3

NT_DATA:        .byte   T_NEW_0-T1_, 0  ;смещение к массиву относительно T1_
                .byte   TCNEW_0-T_      ;смещение к массиву относительно T_
                .byte   T_OLD_0-T1_, 1
                .byte   TCOLD_0-T_
                .byte   T_NEW_1-T1_, 1
                .byte   TCNEW_1-T_
                .byte   T_OLD_1-T1_, 1
                .byte   TCOLD_1-T_
                .byte   T_NEW_2-T1_, 0
                .byte   TCNEW_2-T_
                .byte   T_OLD_2-T1_, 0
                .byte   TCOLD_2-T_
                .byte   T_NEW_3-T1_, 0
                .byte   TCNEW_3-T_
                .byte   T_OLD_3-T1_, 0
                .byte   TCOLD_3-T_
                .even

NT_:            .blkw   96. ;CreatedNoteTableAddress

VAR0END = T1_           ;Init zeroes from VARS to VAR0END-1

MDLADDR:
                .end

