;                        Регистры сопра.
;
;           Регистры управления тональным генератором
;
;│       A   B   C       │       A   B   C       │  0 - 377
;│       R1, R3, R5      │       R0, R2, R4      │  1 - 376
;├──┬──┬──┬──╥──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤  2 - 375
;│15│14│13│12║11│10│09│08│07│06│05│04│03│02│01│00│  3 - 374
;└──┴──┴──┴──╨──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘  4 - 373
; └─────────┘ └─────────────────────────────────┘   5 - 372
; не использ.  период тона тонального генератора
;
;
;              Регистр управления генератором шума
;
;│          R6           │                          6 - 371
;├──┬──┬──╥──┬──┬──┬──┬──┤
;│07│06│05║04│03│02│01│00│
;└──┴──┴──╨──┴──┴──┴──┴──┘
; └──────┘ └────────────┘
;   не      уровень шума
; использ.
;
;
;                 Регистр управления смесителем
;
;│          R7           │                          7 - 370
;├──┬──╥──┬──┬──╥──┬──┬──┤
;│07│06║05│04│03║02│01│00│
;└──┴──╨──┴──┴──╨──┴──┴──┘
;  │  │  │  │  │  │  │  └─ канал A ┐ запрещение подачи частоты
;  │  │  │  │  │  │  └──── канал B │ тона
;  │  │  │  │  │  └─────── канал C ┘
;  │  │  │  │  └────────── канал A ┐ запрещение подачи частоты
;  │  │  │  └───────────── канал B │ шума
;  │  │  └──────────────── канал C ┘
;  │  └─────────────────── порт B  ┐ запрещение функции
;  └────────────────────── порт A  ┘ ввода
;
;
;                 Регистр управления амплитудой
;
;       A    B    C
;│     R10, R11, R12     │                         10 - 367
;├──┬──┬──╥──╥──┬──┬──┬──┤                         11 - 366
;│07│06│05║04║03│02│01│00│                         12 - 365
;└──┴──┴──╨──╨──┴──┴──┴──┘
; └──────┘  │ └─────────┘
;   не      │  амплитуда
; использ.  └───────────── использование огибающей
;
;
;            Регистры управления периодом огибающей
;
;│          R14          │          R13          │ 13 - 364
;├──┬──┬──┬──╥──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤ 14 - 363
;│15│14│13│12║11│10│09│08│07│06│05│04│03│02│01│00│
;└──┴──┴──┴──╨──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
; └─────────────────────────────────────────────┘
;                период огибающей
;
;
;         Регистр управления формой и циклом огибающей
;
;│          R15          │                         15 - 362
;├──┬──┬──┬──╥──┬──┬──┬──┤
;│07│06│05│04║03│02│01│00│
;└──┴──┴──┴──╨──┴──┴──┴──┘
;              │  │  │  └─ задержка
;              │  │  └──── чередование
;              │  └─────── атака
;              └────────── продолжение
;
;
;
;

; блок воспроизведения модуля Sound Tracker 1.xx
; алгоритм не мой, вероятно автор - RDC.
; но поскольку он работает корректно, я не стал изобретать велосипед.

MDLADDR=10000                   ;адрес загрузки ST модуля

;регистры 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.

CHP.EnvelopeFlag            = 0 ;byte - какой-то флаг, выключающий огибающую в каком-то случае
CHP.Number_Of_Notes_To_Skip = 1 ;byte - длительность звучания ноты (коды {241..377} - 241)
CHP.Position_In_Sample      = 2 ;byte - текущая позиция в сэмпле и орнаменте 0 - если коды 0..137
CHP.Note                    = 3 ;byte - номер ноты
CHP.Note_Skip_Counter       = 4 ;byte - буфер длительности
CHP.SamplePointer           = 5 ;word - адрес текущего сэмпла
CHP.OrnamentPointer         = 7 ;word - адрес текущего орнамента
CHP.Sample_Tik_Counter      = 11 ;byte - длина зацикливания в сэмпле 40 - если коды 0..137, 377 - если код 200
CHP = 12    ; размер блока параметров канала


;коды в разделе:
;0..137     - ноты
;140..157   - номер сэмпла
;               нулевого сэмпла нет, но можно на всякий случай заблокировать канал
;160..177   - номер орнамента
;               огибающая отключается
;               при нулевом значении орнаменты отключаются
;200        - пауза (отключить канал, завершить позицию)
;201        - пустая нота (позиция завершается)
;202        - отключение огибающей и орнамента
;203..216   - номер огибающей -0x80 (т.е. огибающие могут иметь номера 3 - 14)
;               (следующий байт значение периода огибающей младший байт)
;               орнаменты отключаются
;217..240   - не существует
;241..376   - значение задержки между нотами в канале (в прерываниях) - 0xA1
;377        - конец раздела

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

2$:             call    Start_Play
                jmp     Load

; это для воспроизведения в основном цикле
Start_Play:     mov     #40000, @#177662    ; запрещаем прерывания по вектору 100
                mov     SP, MSP
                mov     @#4, MV4            ; прерывание по вектору 4
                mov     #NV4, @#4           ; поменяем на своё, чтобы по кнопке СТОП прекращать играть
                mov     #102, @#100         ; меняем вектор
                mov     #2, @#102
                clr     @#177662            ; разрешаем прерывания обратно
                mov     #MDLADDR, R4
                call    BEGIN               ; инициализация переменных модуля воспроизведения

LOOP:           call    PLAY                ; играем квант
                wait                        ; ждём прерывания от вектора 100
                bit     #100, @#177716      ; пока клавиша не нажата
                bne     LOOP                ; продолжаем играть
                ; нажатие любой клавиши - прекращение игры
NV4:            mov     #40000, @#177662    ; запрещаем прерывания по вектору 100
                call    MUTE                ; глушим AY
                mov     MV4, @#4            ; всё восстанавливаем назад
                mov     MSP, SP
                return

; это для воспроизведения в фоне
;Start_Play:     mov     #40000, @#177662    ; запрещаем прерывания по вектору 100
;                mov     @#4, MV4            ; прерывание по вектору 4
;                mov     #NV4, @#4           ; поменяем на своё, чтобы по кнопке СТОП прекращать играть
;                mov     #TRPT, @#100        ; адрес фоновой процедуры воспроизведения
;                mov     #340, @#102         ; адрес вектора
;                mov     #MDLADDR, R4
;                call    BEGIN               ; инициализация переменных модуля воспроизведения
;                clr     @#177662            ; разрешаем прерывания обратно
;                return                      ; и выход, правда там уже надо не на загрузку следующего модуля
;                                            ; выходить, а куда-нибудь, куда надо
;
;NV4:            mov     #40000, @#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    PLAY
                mov     (SP)+, R5
                mov     (SP)+, R4
                mov     (SP)+, R3
                mov     (SP)+, R2
                mov     (SP)+, R1
                mov     (SP)+, R0
                rti

MUTE:           clr     AYREGS + AY.AmplitudeA
                clrb    AYREGS + AY.AmplitudeC
                movb    #77, AYREGS + AY.Mixer
                jmp     ROUT
; Вход: R4 - адрес загрузки ST модуля 
BEGIN:          call    INIT                ; вызовем процедуру инициализации
                                            ; Вход: R4 - адрес загрузки ST модуля 
PLAY:           mov     PC, R3
                add     #AYREGS-., R3
                decb    Cur_Delay           ; нота кончилась?
                bgt     S12                 ; нет - продолжим
                ; возьмём новую ноту
                movb    Delay, Cur_Delay
                mov     PC, R5
                add     #Channel_A-., R5
                call    CheckSkip           ; проверяем длительность, не пора ли
                bpl     1$                  ; брать новую ноту
                mov     Address_In_Pattern_A, R4
                cmpb    (R4), #377
                bne     2$
                clr     R0
                bisb    Cur_Position, R0    ; возьмём номер текущего раздела
                cmpb    R0, Order_Length    ; это последний раздел?
                blo     11$                 ; нет
                clr     R0                  ; да - будет первый
11$:            mov     R0, R4
                incb    R0                  ; и пихнём номер обратно
                movb    R0, Cur_Position
                asl     R4                  ; получим адрес параметров
                add     OrderPtr, R4        ; текущего раздела
                movb    (R4)+, R0           ; номер играемого раздела
                movb    (R4), R1            ; signed byte -> word
                mov     R1, Transp          ; высота
                mov     PatternsPtr, R4
                mov     #7-1, R1            ; длина блока параметров раздела
                call    FindObj             ; ищем нужный раздел в таблице
                call    GETADR
                mov     R2, Address_In_Pattern_A
                call    GETADR
                mov     R2, Address_In_Pattern_B
                call    GETADR
                mov     R2, Address_In_Pattern_C

2$:             mov     Address_In_Pattern_A, R4
                call    PatternInterpreter
                mov     R4, Address_In_Pattern_A

1$:             add     #CHP, R5
                call    CheckSkip
                bpl     3$
                mov     Address_In_Pattern_B, R4
                call    PatternInterpreter
                mov     R4, Address_In_Pattern_B

3$:             add     #CHP, R5
                call    CheckSkip
                bpl     4$
                mov     Address_In_Pattern_C, R4
                call    PatternInterpreter
                mov     R4, Address_In_Pattern_C
4$:
S12:            clrb    AY.Mixer(R3)
                mov     PC, R5
                add     #Channel_A-., R5
                call    SampleCycle         ; обработка зацикливания сэмпла
                                            ; выход: R1 - текущая позиция в сэмпле
                movb    CHP.Sample_Tik_Counter(R5), R0
                incb    R0
                beq     30$
                mov     CHP.SamplePointer(R5), R4
                call    SampleDecode        ; работа с сэмплом
                                            ; вход:  R4 - указатель на начало сэмпла
                                            ;        R1 - текущая позиция в сэмпле
                                            ; выход: R2 - смещение тона
                                            ;        R0 - громкость
                call    ProcOrnament_GetTone    ; вход:  R2 - смещение тона
                                                ; выход: R1 - частота тона
                mov     R1, AY.TonA(R3)
30$:            mov     #AY.AmplitudeA, R4
                call    SetEnvelope         ; вход:  R0 - значение амплитуды
                                            ;        R3 - адрес блока регистров AY
                                            ;        R4 - адрес регистра амплитуды

                asrb    AY.Mixer(R3)
                add     #CHP, R5
                call    SampleCycle         ; обработка зацикливания сэмпла
                                            ; выход: R1 - текущая позиция в сэмпле
                movb    CHP.Sample_Tik_Counter(R5), R0
                incb    R0
                beq     31$
                mov     CHP.SamplePointer(R5), R4
                call    SampleDecode        ; работа с сэмплом
                                            ; вход:  R4 - указатель на начало сэмпла
                                            ;        R1 - текущая позиция в сэмпле
                                            ; выход: R2 - смещение тона
                                            ;        R0 - громкость
                call    ProcOrnament_GetTone    ; вход:  R2 - смещение тона
                                                ; выход: R1 - частота тона
                mov     R1, AY.TonB(R3)
31$:            mov     #AY.AmplitudeB, R4
                call    SetEnvelope         ; вход:  R0 - значение амплитуды
                                            ;        R3 - адрес блока регистров AY
                                            ;        R4 - адрес регистра амплитуды

                asrb    AY.Mixer(R3)
                add     #CHP, R5
                call    SampleCycle         ; обработка зацикливания сэмпла
                                            ; выход: R1 - текущая позиция в сэмпле
                movb    CHP.Sample_Tik_Counter(R5), R0
                incb    R0
                beq     32$
                mov     CHP.SamplePointer(R5), R4
                call    SampleDecode        ; работа с сэмплом
                                            ; вход:  R4 - указатель на начало сэмпла
                                            ;        R1 - текущая позиция в сэмпле
                                            ; выход: R2 - смещение тона
                                            ;        R0 - громкость
                call    ProcOrnament_GetTone    ; вход:  R2 - смещение тона
                                                ; выход: R1 - частота тона
                mov     R1, AY.TonC(R3)
32$:            mov     #AY.AmplitudeC, R4
                call    SetEnvelope         ; вход:  R0 - значение амплитуды
                                            ;        R3 - адрес блока регистров AY
                                            ;        R4 - адрес регистра амплитуды
; заполнение регистров сопра данными из памяти
ROUT:           mov     PC, R4
                add     #AYREGS + AY.EnvelopeType-., R4   ; адрес регистра R15
                mov     #AY.EnvelopeType, R0        ; количество регистров
                tstb    (R4)                        ; волпак есть?
                bne     33$                         ; если есть - переход
                dec     R0                          ; если нет пропустим
                cmp     -(R0), -(R4)                ; область для волпака
34$:            dec     R4
33$:            mov     R0, R1
                comb    R1
                mov     R1, @#177714
                movb    (R4), R1
                comb    R1
                movb    R1, @#177714
                dec     R0
                bpl     34$
                return

; поиск нужного объекта в цепочке
; вход:  R0 - номер искомого объекта
;        R1 - размер объекта - 1
; выход: R4 - адрес найденного объекта + 1 (как раз данные)
FindObj:        cmpb    (R4)+, R0
                beq     05Ret$
                add     R1, R4
                br      FindObj

; проверка не пора ли получать новую ноту
CheckSkip:      decb    CHP.Note_Skip_Counter(R5)
                bpl     05Ret$
                movb    CHP.Number_Of_Notes_To_Skip(R5), CHP.Note_Skip_Counter(R5)
                sen
05Ret$:         return

; получение адреса по смещению, на которое указывает R4
; вход:  R4 - текущий адрес в файле
; выход: R2 - адрес
GETADR:         clr     R2
                bisb    (R4)+, R2
                swab    R2
                bisb    (R4)+, R2
                swab    R2
                add     Module_Address, R2
                return

; вход:  R0 - значение амплитуды
;        R3 - адрес блока регистров AY
;        R4 - адрес регистра амплитуды
SetEnvelope:    add     R3, R4
                movb    R0, (R4)
                tstb    CHP.Sample_Tik_Counter(R5)
                bmi     24$
                movb    CHP.EnvelopeFlag(R5), R0
                beq     24$
                cmpb    #2, R0
                beq     26$
                movb    #2, CHP.EnvelopeFlag(R5)
                br      25$

26$:            clrb    AY.EnvelopeType(R3)
25$:            bisb    #20, (R4)
24$:            return

; вход:  R2 - смещение тона
; выход: R1 - частота тона
ProcOrnament_GetTone:
                mov     OrnamentCurPos, R1
                add     CHP.OrnamentPointer(R5), R1 ; указатель текущего орнамента
                movb    (R1), R1            ; берём данные орнамента из текущей позиции signed byte-> word
                movb    CHP.Note(R5), R4    ; берём текущую ноту (0..137)
                add     R4, R1              ; прибавляем текущую ноту
                add     Transp, R1          ; прибавляем текущую высоту всего раздела
                bpl     1$
                clr     R1
1$:             cmp     R1, #140
                blo     2$
                mov     #137, R1
2$:             asl     R1                  ; *2
                add     PC, R1
                add     #TABLE-., R1
                mov     (R1), R1            ; получаем тон.
                tst     R2                  ; чё знак?
                bpl     35$
                bic     #100000, R2         ; прибавлять надо
                add     R2, R1
                return

35$:            sub     R2, R1              ; вычитать.
                return


; работа с сэмплом
; вход:  R4 - указатель на начало сэмпла
;        R1 - текущая позиция в сэмпле
; выход: R2 - смещение тона
;        R0 - громкость
SampleDecode:   mov     R1, R2
                asl     R1
                add     R1, R2              ; умножаем на 3
                bic     #177400, R2
                add     R2, R4              ; перемещаемся к данным
                movb    (R4)+, R0           ; байт 0 - громкость и старшие 4бита смещения тона
                movb    (R4)+, R2           ; байт 1 - шум и маски
                mov     #20004, R1          ; формируем биты для микшера мл.байт - тон, ст.байт - шум
                bitb    #200, R2            ; маска шума (0 - включен, 1 - выключен)
                bne     6$                  ; выкл.
                bic     #20000, R1          ; вкл.
6$:             bitb    #100, R2            ; маска тона (0 - включен, 1 - выключен)
                bne     7$                  ; выкл.
                bic     #4, R1              ; вкл.
7$:             bic     #177740, R2         ; значение шума
                ; формируем биты в микшере
                bisb    R1, AY.Mixer(R3)
                swab    R1
                bisb    R1, AY.Mixer(R3)
                ; теперь в мл байте R1 - маска шума
                tstb    R1                  ; Шум включен?
                bne     1$                  ; выключен
                movb    R2, AY.Noise(R3)    ; и помещаем его в регистр.
1$:             clr     R2
                bisb    (R4), R2            ; байт 2 - смещение тона (младший байт)
                movb    R0, R1
                bic     #177417, R1         ; биты 7..4 - это 4ре старших бита смещения тона
                bic     #360, R0            ; выделяем громкость - биты 3..0
                asl     R1
                asl     R1
                asl     R1
                asl     R1
                bis     R1, R2
                bitb    #40, -(R4)          ; куда смещение тона? (1 - плюс к частоте, 0 - минус к частоте)
                beq     10$
                bis     #100000, R2         ; знак
10$:            return

; обработка зацикливания сэмпла
; выход: R1 - текущая позиция в сэмпле
SampleCycle:    movb    CHP.Position_In_Sample(R5), R0  ; берём текущую позицию в сэмпле
                movb    R0, R1
                tstb    CHP.Sample_Tik_Counter(R5)      ; берём длину зацикливания сэмпла
                bmi     1$
                incb    R0                  ; увеличиваем
                bic     #177740, R0
                movb    R0, CHP.Position_In_Sample(R5)  ; сохраняем
                decb    CHP.Sample_Tik_Counter(R5) ; уменьшаем счётчик зацикливания
                bne     1$                  ; пока не кончится
                mov     CHP.SamplePointer(R5), R4 ; иначе
                add     #96., R4
                movb    (R4)+, R0           ; берём позицию зацикливания
                beq     PT_Pause            ; если 0, то канал отключается по завершении сэмпла
                movb    R0, R1
                decb    R1
                bic     #177740, R0
                movb    R0, CHP.Position_In_Sample(R5)  ; сохраняем
                movb    (R4), R0            ; количество позиций в повторе
                incb    R0
                movb    R0, CHP.Sample_Tik_Counter(R5) ; восстанавливаем счётчик
1$:             movb    R1, OrnamentCurPos
                return

PatternInterpreter:
                movb    (R4)+, R0
                cmpb    R0, #140
                blo     0NOTE$
                cmpb    R0, #160
                blo     0SMPL$
                cmpb    R0, #200
                blo     0ORNM$
                beq     PT_Pause
                cmpb    R0, #201
                beq     0Empty$
                cmpb    R0, #202
                beq     0OrnEnv_Off$
                cmpb    R0, #217
                blo     0Envelope$
                sub     #241, R0
                movb    R0, CHP.Note_Skip_Counter(R5)
                movb    R0, CHP.Number_Of_Notes_To_Skip(R5)
                br      PatternInterpreter

0NOTE$:         movb    R0, CHP.Note(R5)    ; ноты
                clrb    CHP.Position_In_Sample(R5)
                movb    #40, CHP.Sample_Tik_Counter(R5)
0Empty$:        return

0SMPL$:         sub     #140, R0            ; samples
                mov     R4, R2
                mov     #99.-1, R1          ; длина сэмпла
                mov     SamplesPtr, R4
                call    FindObj
                mov     R4, CHP.SamplePointer(R5)
                mov     R2, R4
                br      PatternInterpreter

PT_Pause:       movb    #-1, CHP.Sample_Tik_Counter(R5)
                return

0Envelope$:     sub     #200, R0
                movb    R0, AY.EnvelopeType(R3)
                movb    (R4)+, AY.Envelope(R3)
                movb    #1, CHP.EnvelopeFlag(R5)
                clr     R0
                br      21$

0OrnEnv_Off$:   clr     R0
                br      16$

0ORNM$:         sub     #160, R0
16$:            clrb    CHP.EnvelopeFlag(R5)
21$:            mov     R4, R2
                mov     #33.-1, R1          ; длина орнамента
                mov     OrnamentPtr, R4
                call    FindObj
                mov     R4, CHP.OrnamentPointer(R5)
                mov     R2, R4
                br      PatternInterpreter


TABLE:          .word   7370, 7020, 6540, 6200, 5730, 5450, 5210, 4760
                .word   4540, 4340, 4130, 3740

                .word   3574, 3410, 3260, 3100, 2754, 2624, 2504, 2370
                .word   2260, 2160, 2054, 1760

                .word   1676, 1604, 1530, 1440, 1366, 1312, 1242, 1174
                .word   1130, 1070, 1026, 770

                .word   737, 702, 654, 620, 573, 545, 521, 476
                .word   454, 434, 413, 374

                .word   357, 341, 326, 310, 275, 262, 250, 237
                .word   226, 216, 205, 176

                .word   167, 160, 153, 144, 136, 131, 124, 117
                .word   113, 107, 102, 77

                .word   73, 70, 65, 62, 57, 54, 52, 47, 45, 43, 41, 37

                .word   35, 34, 32, 31, 27, 26, 25, 23, 22, 21, 20, 17

                .word   0
; Вход: R4 - адрес загрузки ST модуля 
INIT:           mov     R4, Module_Address
                mov     PC, R3
                add     #Delay-., R3
                movb    (R4)+, (R3)+        ; темп смещение +0
                call    GETADR              ; получим адрес списка позиций (Ордера)
                movb    (R2)+, R0           ; оттуда прочтём количество разделов в ордере
                incb    R0
                movb    R0, (R3)+           ; число разделов

                mov     R2, (R3)+           ; адрес ордера
                call    GETADR
                mov     R2, (R3)+           ; адрес таблицы орнаментов
                mov     R2, -(SP)
                call    GETADR
                mov     R2, (R3)+           ; адрес таблицы адресов разделов
                mov     Module_Address, R4
                add     #27., R4
                mov     R4, (R3)+           ; адрес таблицы сэмплов
                mov     PC, R4
                add     #WORK-., R4
                mov     R4, Address_In_Pattern_A
                movb    #377, (R4)+         ; признак конца
                mov     #CHP+CHP+CHP+14., R1    ; очищаем рабочую область
36$:            clrb    (R4)+
                sob     R1, 36$
                mov     (SP)+, R4

                mov     #33.-1, R1          ; длина орнамента
                clr     R0
                call    FindObj             ; ищем нулевой орнамент
                decb    R0  ;-1

                movb    R0, Channel_A + CHP.Sample_Tik_Counter    ;признак
                movb    R0, Channel_B + CHP.Sample_Tik_Counter    ;что нет зацикливания в текущем сэмпле
                movb    R0, Channel_C + CHP.Sample_Tik_Counter    ;
                movb    #1, Cur_Delay
                mov     R4, Channel_A + CHP.OrnamentPointer       ;начальный орнамент
                mov     R4, Channel_B + CHP.OrnamentPointer       ;
                mov     R4, Channel_C + CHP.OrnamentPointer       ;
                jmp     ROUT

Module_Address: .word   0   ; адрес загрузки ST модуля
Transp:         .word   0
OrnamentCurPos: .word   0
Address_In_Pattern_A: .word 0 ; адрес ноты в текущем паттерне канала A
Address_In_Pattern_B: .word 0 ; адрес ноты в текущем паттерне канала B
Address_In_Pattern_C: .word 0 ; адрес ноты в текущем паттерне канала C

Cur_Delay:      .byte   0   ; буфер темпа исполнения
Cur_Position:   .byte   0   ; тут находится номер текущего раздела (byte)
Delay:          .byte   0   ; темп исполнения
Order_Length:   .byte   0   ; длина цепочки разделов
OrderPtr:       .word   0   ; адрес таблицы позиций разделов
;                       формат: .byte количество разделов в таблице
;                               .byte pattern  ┐ по числу позиций
;                               .byte height   ┘
OrnamentPtr:    .word   0   ; адрес таблицы орнаментов
;                       формат: .byte номер орнамента
;                               .blkb 32.
PatternsPtr:    .word   0   ; адрес таблицы размещения паттернов
;                       формат: .byte порядковый номер раздела
;                               .word addr patt A ┐по числу позиций
;                               .word addr patt B │
;                               .word addr patt C ┘
SamplesPtr:     .word   0   ; адрес таблицы сэмплов
;                       формат: .byte номер сэмпла
;                               .blkb 98.

WORK:           .byte   377
;блоки параметров начинаются с нечётного адреса, это надо учитывать
Channel_A:      .blkb   CHP ; блок параметров канала A
Channel_B:      .blkb   CHP ; блок параметров канала B
Channel_C:      .blkb   CHP ; блок параметров канала C
                .even
AYREGS:         .blkb   14. ; область регистров сопра, начинается с чётного адреса

                .end


