
;регистры 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
STP.Position_In_Sample      = 0     ;byte
STP.Position_In_Ornament    = 1     ;byte
STP.Current_Ton_Sliding     = 2     ;word
STP.Glissade                = 4     ;word
STP.Address_In_Pattern      = 6     ;word
STP.Note                    = 8.    ;word
STP.Number_Of_Notes_To_Skip = 10.   ;byte
STP.Note_Skip_Counter       = 11.   ;byte
STP.Volume                  = 12.   ;byte
                            ; 13.   ;byte свободно
STP.Enabled                 = 14.   ;byte
STP.Envelope_Enabled        = 15.   ;byte
STP.Loop_Ornament_Position  = 16.   ;byte
STP.Ornament_Length         = 17.   ;byte
STP.OrnamentPointer         = 18.   ;word
STP.Loop_Sample_Position    = 20.   ;byte
STP.Sample_Length           = 21.   ;byte
STP.SamplePointer           = 22.   ;word
STP.Tone_Reg                = 24.   ;byte Tone_Reg + Ampl_Reg должны быть выровнены по слову
STP.Ampl_Reg                = 25.   ;byte
STP = 26.

E36BP=320
MDLADDR=10000
;загрузка модуля.
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     #42400, @#177662    ; запрещаем прерывания по вектору 100
                mov     SP, MSP
                mov     @#4, MV4            ; прерывание по вектору 4
                mov     #NV4, @#4           ; поменяем на своё, чтобы по кнопке СТОП прекращать играть
                mov     #102, @#100         ; меняем вектор
                mov     #2, @#102
                mov     #MDLADDR, R1
                call    STP_Init
                mov     #2400, @#177662
1$:             call    STP_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    STP_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    STP_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     STP_ROut


STP_Play:       decb    DelayCounter                ;уменьшаем текущий счётчик темпа
                bgt     1$
                movb    Delay, DelayCounter
                mov     PC, R5
                add     #Channel_A-., R5
                decb    STP.Note_Skip_Counter(R5)
                bpl     0SkipA$
                mov     STP.Address_In_Pattern(R5), R4
                tstb    (R4)
                bne     3$
                mov     STP_PositionsPointer, R3
                clr     R0
                bisb    CurrentPosition, R0
                incb    R0
                cmpb    R0, (R3)+                   ;конца достигли?
                bne     2$
                movb    (R3), R0                    ;да - тогда берём позицию зацикливания
2$:             movb    R0, CurrentPosition
                inc     R3
                asl     R0
                add     R0, R3
                clr     R0
                bisb    (R3)+, R0                   ;тут номер нужного раздела *6
                movb    (R3)+, R1                   ;transposition signed byte -> word
                mov     R1, Transp
                add     STP_PatternsPointer, R0     ;находим нужный раздел в таблице разделов
                mov     Module_Address, R3
                clr     R4
                bisb    (R0)+, R4
                swab    R4
                bisb    (R0)+, R4
                swab    R4
                add     R3, R4

                clr     R1
                bisb    (R0)+, R1
                swab    R1
                bisb    (R0)+, R1
                swab    R1
                add     R3, R1
                mov     R1, Channel_B + STP.Address_In_Pattern
                clr     R1
                bisb    (R0)+, R1
                swab    R1
                bisb    (R0)+, R1
                swab    R1
                add     R3, R1
                mov     R1, Channel_C + STP.Address_In_Pattern

3$:             call    STP_PatternInterpreter
                mov     R4, STP.Address_In_Pattern(R5)

0SkipA$:        add     #STP, R5
                decb    STP.Note_Skip_Counter(R5)
                bpl     0SkipB$
                mov     STP.Address_In_Pattern(R5), R4
                call    STP_PatternInterpreter
                mov     R4, STP.Address_In_Pattern(R5)

0SkipB$:        add     #STP, R5
                decb    STP.Note_Skip_Counter(R5)
                bpl     0SkipC$
                mov     STP.Address_In_Pattern(R5), R4
                call    STP_PatternInterpreter
                mov     R4, STP.Address_In_Pattern(R5)

0SkipC$:
1$:             mov     PC, R4
                add     #AYREGS-., R4
                mov     PC, R5
                add     #Channel_A-., R5
                clrb    AY.Mixer(R4)
                call    STP_GetRegisters
                call    STP_GetRegisters
                call    STP_GetRegisters

;вывод регистров
STP_ROut:       mov     #177714, R5
                mov     #362, R1
                mov     PC, R4
                add     #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

;вход:  R5 - адрес блока параметров канала
;       R4 - адрес в разделе
STP_PatternInterpreter:
0Loop:          clr     R0
                bisb    (R4)+, R0
                cmpb    R0, #0xf0
                beq     0Gliss
                bhi     0Vol
                cmpb    R0, #0xe0
                bhis    0Ret
                cmpb    R0, #0xd0
                bhis    0Rel
                cmpb    R0, #0xc0
                beq     0Env0
                bhi     0Env
                cmpb    R0, #0x80
                bhis    0SkipN
                cmpb    R0, #0x70
                bhis    0Ornam
                cmpb    R0, #0x61
                bhis    0Sampl
                dec     R0          ;нота
                mov     R0, STP.Note(R5)
                clrb    STP.Position_In_Sample(R5)
                clrb    STP.Position_In_Ornament(R5)
                clr     STP.Current_Ton_Sliding(R5)
                movb    #-1, STP.Enabled(R5)
                br      0Ret

0Gliss:         movb    (R4)+, R0
                mov     R0, STP.Glissade(R5)
                br      0Loop

0Vol:           sub     #0xf1, R0
                movb    R0, STP.Volume(R5)
                br      0Loop

0Rel:           clrb    STP.Enabled(R5)
0Ret:           movb    STP.Number_Of_Notes_To_Skip(R5), STP.Note_Skip_Counter(R5)
                return

0Env:           sub     #0xc0, R0
                movb    R0, AYREGS + AY.EnvelopeType
                movb    (R4)+, AYREGS + AY.Envelope ;только мл.байт
0Env0:          movb    #0x10, STP.Envelope_Enabled(R5)
                clrb    STP.Loop_Ornament_Position(R5)
                clr     STP.Glissade(R5)
                movb    #1, STP.Ornament_Length(R5)
                br      0Loop

0SkipN:         sub     #0x80, R0
                movb    R0, STP.Number_Of_Notes_To_Skip(R5)
                br      0Loop

0Ornam:         sub     #0x70, R0
                asl     R0
                add     STP_OrnamentsPointer, R0
                call    GetAddr
                movb    (R1)+, STP.Loop_Ornament_Position(R5)
                movb    (R1)+, STP.Ornament_Length(R5)
                mov     R1, STP.OrnamentPointer(R5)
                clrb    STP.Envelope_Enabled(R5)
                movb    #-1, AYREGS + AY.EnvelopeType
                clr     STP.Glissade(R5)
                br      0Loop

0Sampl:         sub     #0x61, R0
                asl     R0
                add     STP_SamplesPointer, R0
                call    GetAddr
                movb    (R1)+, STP.Loop_Sample_Position(R5)
                movb    (R1)+, STP.Sample_Length(R5)
                mov     R1, STP.SamplePointer(R5)
                br      0Loop

; вход: R4 - адрес блока параметров регистров
; вход: R5 - адрес блока параметров канала
STP_GetRegisters:
                clr     R3              ; амплитуда по умолчанию
                mov     #44, R2         ; маска микшера по умолчанию
                tstb    STP.Enabled(R5)
                beq     0Skip
                add     STP.Glissade(R5), STP.Current_Ton_Sliding(R5)
                mov     STP.Note(R5), R3
                add     Transp, R3
                tstb    STP.Envelope_Enabled(R5)
                bne     1$
                movb    STP.Position_In_Ornament(R5), R0
                add     STP.OrnamentPointer(R5), R0
                movb    (R0), R0
                add     R0, R3
1$:             tst     R3
                bpl     2$
                clr     R3
2$:             cmp     R3, #96.
                blo     3$
                mov     #95., R3
3$:             asl     R3
                add     PC, R3
                add     #ST_Table-., R3
                mov     (R3), -(SP)
                movb    STP.Position_In_Sample(R5), R0
                asl     R0
                asl     R0
                add     STP.SamplePointer(R5), R0
                movb    (R0)+, R2       ; b0
                movb    (R0)+, R1       ; b1
                clr     R3
                bisb    (R0)+, R3
                swab    R3
                bisb    (R0)+, R3
                swab    R3              ; приращение тона в сэмпле
                add     (SP)+, R3
                add     STP.Current_Ton_Sliding(R5), R3
                bic     #0xf000, R3     ; всё, в R3 готовый тон
                movb    STP.Tone_Reg(R5), R0
                add     R4, R0
                mov     R3, (R0)        ; сохраняем тон

                mov     R2, R3
                bic     #0xfff0, R3     ; амплитуда
                movb    STP.Volume(R5), R0
                sub     R0, R3
                bpl     4$
                clr     R3
4$:             bitb    #1, R1          ; маска огибающей есть?
                beq     5$
                bisb    STP.Envelope_Enabled(R5), R3
5$:             ; всё, в R3 готовая амплитуда
                tstb    R2              ; маска шума есть?
                bmi     6$
                asrb    R1
                bic     #0xffe0, R1
                movb    R1, AY.Noise(R4)
6$:             asrb    R2
                asrb    R2
                bic     #177733, R2     ; Это биты маски микшера
;остались свободными регистры R0 R1
                movb    STP.Position_In_Ornament(R5), R0
                incb    R0
                cmpb    R0, STP.Ornament_Length(R5)
                blo     7$
                movb    STP.Loop_Ornament_Position(R5), R0
7$:             movb    R0, STP.Position_In_Ornament(R5)

                movb    STP.Position_In_Sample(R5), R0
                incb    R0
                cmpb    R0, STP.Sample_Length(R5)
                blo     8$
                movb    STP.Loop_Sample_Position(R5), R0
                bpl     8$
                clrb    STP.Enabled(R5)
8$:             movb    R0, STP.Position_In_Sample(R5)

0Skip:          asrb    AY.Mixer(R4)
                bisb    R2, AY.Mixer(R4)
                movb    STP.Ampl_Reg(R5), R0
                add     R4, R0
                movb    R3, (R0)    ; сохраняем амплитуду
                add     #STP, R5
                return

;вход:  R1 - адрес загрузки модуля
STP_Init:       mov     R1, Module_Address
                mov     R1, R0
                movb    (R0)+, Delay
                call    GetAddr
                mov     R1, STP_PositionsPointer
                call    GetAddr
                mov     R1, STP_PatternsPointer
                call    GetAddr
                mov     R1, STP_OrnamentsPointer
                mov     R1, R4
                call    GetAddr
                mov     R1, STP_SamplesPointer
                mov     R1, R0
                call    GetAddr
                mov     R1, R3
                mov     R4, R0
                call    GetAddr
                mov     R1, R4
                mov     PC, R0
                add     #VARS-., R0
                mov     #VARS_End-VARS, R1
1$:             clrb    (R0)+
                sob     R1, 1$
                movb    #-1, CurrentPosition
                mov     PC, R0
                add     #NullPtr-., R0
                mov     PC, R5
                add     #Channel_A-., R5
                mov     (PC)+, R1
                .byte   AY.TonA, AY.AmplitudeA
                call    FillSTP
                mov     (PC)+, R1
                .byte   AY.TonB, AY.AmplitudeB
                call    FillSTP
                mov     (PC)+, R1
                .byte   AY.TonC, AY.AmplitudeC
                call    FillSTP
                jmp     STP_ROut

FillSTP:        mov     R0, STP.Address_In_Pattern(R5)
                movb    (R3)+, STP.Loop_Sample_Position(R5)
                movb    (R3)+, STP.Sample_Length(R5)
                mov     R3, STP.SamplePointer(R5)
                tst     -(R3)
                movb    (R4)+, STP.Loop_Ornament_Position(R5)
                movb    (R4)+, STP.Ornament_Length(R5)
                mov     R4, STP.OrnamentPointer(R5)
                tst     -(R4)
                mov     R1, STP.Tone_Reg(R5)
                add     #STP, R5
                return
;получение адреса по смещению, на которое указывает R0
;вход:  R0 - текущий адрес в файле
;выход: R1 - адрес
GetAddr:        clr     R1
                bisb    (R0)+, R1
                swab    R1
                bisb    (R0)+, R1
                swab    R1
                add     Module_Address, R1
                return

ST_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

NullPtr:        .word   0

STP_PositionsPointer:   .word   0
STP_PatternsPointer:    .word   0   ;начало таблицы разделов
STP_OrnamentsPointer:   .word   0   ;начало таблицы орнаментов
STP_SamplesPointer:     .word   0   ;начало таблицы сэмплов
Module_Address:         .word   0

Delay:                  .byte   0
                        .even
VARS:
DelayCounter:           .byte   0
CurrentPosition:        .byte   0   ;адрес текущей позиции в ордере
Transp:                 .word   0


Channel_A:      .blkb STP
Channel_B:      .blkb STP
Channel_C:      .blkb STP
                .even
AYREGS:         .blkb 16.
VARS_End:

                .end


