device zxspectrum128 ; ============================================================ ; КОНСТАНТЫ ; ============================================================ VMODE: .T40: EQU #02 .T80: EQU #03 .V320: EQU #81 .V640: EQU #82 .background: EQU #50 .sprite: EQU #50 BANKSIZE EQU #0A ; ---- Константы портов ---- CTC_CH2: EQU #12 CTC_CH3: EQU #13 ; ---- Порты управления страницами фастрам ---- FastRAM: .ON EQU #FB .OFF EQU #7B .SLOT0 EQU #5C SYS_PORT: .ON EQU #7C .OFF EQU #3C ; ---- Коды частоты звука ---- SOUND_8KHZ EQU 0 SOUND_11KHZ EQU 1 SOUND_16KHZ EQU 2 SOUND_22KHZ EQU 3 ; ============================================================ ; ПОДКЛЮЧАЕМ INCLUDE-ФАЙЛЫ ; ============================================================ include "include\head.asm" include "include\keyboard.asm" include "include\dss_equ.asm" include "include\bios_equ.asm" include "include\sp_equ.asm" ; ============================================================ ; ТОЧКА ВХОДА ; ============================================================ begin: jp main ; ============================================================ ; ДАННЫЕ ; ============================================================ ; ============================================================ ; ЗАГОЛОВОК .SPR ; ============================================================ HEAD_SIGN EQU 0 ; 3 bytes "SPR" HEAD_WIDTH EQU 3 ; 2 bytes ширина HEAD_HEIGHT EQU 5 ; 1 byte высота HEAD_OFFSET_Y EQU 6 ; 1 byte отступ Y HEAD_OFFSET_X EQU 7 ; 1 byte отступ X HEAD_FPS EQU 8 ; 1 byte FPS HEAD_SOUNDRATE EQU 9 ; 1 byte код частоты звука HEAD_PALETTE EQU 10 ; 1024 байта палитра HEAD_AUDIO_OFF EQU 1034 ; 4 bytes смещение до аудио HEAD_PART_SIZE EQU 1038 ; 2 bytes размер 1/5 части кадра HEAD_MFP_SIZE EQU 1040 ; 2 bytes размер таблицы смещений HEAD_MFP_PAGES EQU 1042 ; 1 byte страниц под таблицу HEAD_MFP_LAST EQU 1043 ; 2 bytes размер последней страницы HEADER_SIZE EQU 1045 ; размер заголовка MFP_START = HEADER_SIZE ; = 1045 - здесь начинается таблица смещений ; ============================================================ ; БУФЕР ЗАГОЛОВКА ; ============================================================ header: .signature: db "SPR" .width: dw 0 .height: db 0 .offset_y: db 0 .offset_x: db 0 .fps: db 0 .soundrate: db 0 .palette: ds 1024 .sound_off: dw 0,0 .part_size: dw 0 ; размер 1/5 части .mfp_size: dw 0 ; размер таблицы .mfp_pages: db 0 ; страниц под таблицу .mfp_last: dw 0 ; размер последней страницы ; ============================================================ ; Переменные файлового ввода-вывода ; ============================================================ idfile: db 0 ; временный ID файла scr_id: db 0 ; ID файла видео snd_id: db 0 ; ID файла звука buffer: db 0 ; базовая страница выделенной памяти SavePage: db 0 ; сохранённая страница окна 0 win1: db 0 ; сохранённая страница окна 1 sound_load: db 0 ; флаг: нужно ли загружать звук counter_notes: dw #1000 ; счётчик записей в таблице смещений. После каждой загрузки #1000 addres_wav: dw #4000 ; текущий адрес звуковых данных в окне #4000 ; ---- Строки для вывода ---- About: db #0d, #0a, "(c) Sprinter Video Player v0.15.035 Alpha by kostya261. 09.07.2026", #0d, #0a, #00 new_line: db #0a, #0d, #00 cmd: ds #100 commandLine: dw #8082 ; ---- Основной цикл ---- main: ; ---- Выводим информацию о плеере ---- ld hl,About ld c,Dss.PChars rst DssRst ; ---- Разбираем командную строку ---- ld hl,(commandLine) ld de,cmd ld bc,#0745 rst DssRst ; ---- Выводим информацию командной строки ---- ld hl,cmd ld c,Dss.PChars rst DssRst ; ============================================ ; Нужно продумать какие страницы куда пойдут. ; ============================================ ; Немного переборщил. Нужно раза в два меньше. ; но пока пускай будут. Мало ли, пригодяться... ; Запрашиваем память (11 страниц) ld b,BANKSIZE + 1 ld c,Dss.GetMem rst DssRst ld (buffer),a ld b,0 metka_p: djnz metka_p ; ---- Устанавливаем графический режим ---- ld a,#5c: ld (Graphics.video_page),a ld a,#81: call Graphics call LoadHeader ld hl,header.palette call SetPalette call Copy_Palette call ExtractMemoryPage call ClearSound call SetUp_CovoxBL ; call SetUp_Interrupt ; ---- Ищем самый тёмный цвет ---- ld hl, header.palette call FindDarkestColor ld (clrscr.color), a ; сохраняем номер цвета ; ---- Очищаем экран ---- call clrscr call LoadHeader ; ============================================ ; Открываем видеопоток. ; ============================================ ld hl,cmd call OpenFile ld (LoadMoveFP.move_fp),a call LoadMoveFP ld hl,cmd call OpenFile ld (scr_id),a ld (DrawFrame.idfile1),a ld (DrawFrame.idfile1_2),a ld (DrawFrame.idfile1_3),a ld (DrawFrame.idfile1_4),a ld (DrawFrame.idfile1_5),a ;============================================== ld hl, cmd call OpenFile ld (snd_id),a ld (LoadBlockWav.snd_id),a ld (LoadBlockWav_.snd_id),a call Move_FP_Sound call LoadBlockWav ; загружаем первую половину call LoadBlockWav_ ; загружаем вторую половину call SetUp_Interrupt metka_cinema: di: IM 2: ei; Свои прерывания call Flip call DrawFrame ; ---- решаем, нужно ли загружать звук ---- ld a,(sound_load): cp 0: jr nz,.no_skip2: ld a,(next_data.trigger): cp 0: jr nz,.nn1 call LoadBlockWav: jr .nn2 .nn1: call LoadBlockWav_ .nn2: ld a,1: ld (sound_load),a; сбрасываем флаг загрузки звука .no_skip2: di: IM 1: ei; возвращаем дос прерывания halt ld c,Dss.ScanKey: rst DssRst jp nz,_exit ld a,(DrawFrame.exit_video) cp #ff jr nz,metka_cinema ; ============================================ ; ВЫХОД ИЗ ПРОГРАММЫ ; ============================================ _exit: call clear_covox ld a,(LoadMoveFP.move_fp) ld (idfile),a call CloseFile ld a,(scr_id) ld (idfile),a call CloseFile ld a,(snd_id) ld (idfile),a call CloseFile ld c,Dss.Exit rst DssRst ret ; ============================================================ ; ПОИСК САМОГО ТЁМНОГО ЦВЕТА В ПАЛИТРЕ ; ============================================================ ; Вход: HL = указатель на палитру (header.palette) ; Выход: A = номер самого тёмного цвета (0-255) ; BC = яркость самого тёмного цвета (сумма R+G+B) ; ============================================================ FindDarkestColor: ld b, 0 ; номер текущего цвета (0-255) ld de, 0xFFFF ; минимальная яркость (пока максимальная) ld c, 0 ; номер самого тёмного цвета (пока 0) .loop: ; ---- Читаем 4 байта цвета (B, G, R, 0) ---- ld a, (hl) ; B inc hl ld d, a ; сохраняем B в D ld a, (hl) ; G inc hl ld e, a ; сохраняем G в E ld a, (hl) ; R inc hl inc hl ; пропускаем 0 ; ---- Считаем яркость = R + G + B (16 бит!) ---- ld h, 0 ld l, a ; HL = R add hl, de ; HL = R + G ld d, 0 ld e, d ; DE = 0 add hl, de ; HL = R + G + B (теперь яркость в HL) ; ---- Сравниваем с текущей минимальной яркостью ---- ld a, h ; сравниваем старшие байты cp d jr c, .darker ; если HL < DE, то это темнее jr nz, .not_darker ; если HL > DE, пропускаем ld a, l ; сравниваем младшие байты cp e jr nc, .not_darker ; если HL >= DE, пропускаем .darker: ; ---- Это самый тёмный цвет на данный момент ---- ld de, hl ; сохраняем новую минимальную яркость в DE ld c, b ; сохраняем номер цвета .not_darker: ; ---- Следующий цвет ---- inc b ld a, b cp 255 jr nz, .loop ; ---- Возвращаем результат ---- ld a, c ; A = номер самого тёмного цвета ld b, d ; B = старший байт яркости ld c, e ; C = младший байт яркости ret ; ============================================ ; УСТАНОВКА ГРАФИЧЕСКОГО РЕЖИМА ; ============================================ ; в А - номер режима (81 = 320*256б 82 = 640*256) ; ld a,#50 - номер подключаемой видеостраницы, по умолчанию #50 ; Пример вызова: ld a,#5c ; ld (video_page),a ; ld a,#81 ; call Graphics Graphics: ld bc,#0050: rst DssRst .video_page: equ $+1: ld a,#50 out (EmmWin.P3),a ret ; ============================================ ; Загружаем и отрисовываем кадр частями ; ============================================ DrawFrame: .pageA: equ $+1: ld a,#00: out(#a2),a push de .idfile1: equ $+1: ld a,#00 ld hl,#4000 .size_file_1: equ $+1: ld de,#4000 ld c,Dss.Read rst DssRst pop de ld de,0 .buffer_screen: equ $+1: ld hl,#c000 add hl,de: ld (.x),hl ld hl,#4000 ld b,64; Ширина сегмента .y1: equ $+1: ld a,55: out (Y_PORT),a .x: equ $+1: ld de,#0000 di .m1: ld l,l: ld a,(hl): ld a,a: ld (de),a: ld b,b: inc de push de .hght1: equ $+1: ld de,50: add hl,de pop de .y_1: equ $+1: ld a,55: out (Y_PORT),a djnz .m1 ei push de .idfile1_2: equ $+1: ld a,#00 ld hl,#4000 .size_file_2: equ $+1: ld de,#4000 ld c,Dss.Read rst DssRst pop de ld hl,#4000 ld b,64; Ширина сегмента .y2: equ $+1: ld a,55: out (Y_PORT),a .m2: di ld l,l: ld a,(hl): ld a,a: ld (de),a: ld b,b: inc de push de .hght2: equ $+1: ld de,50: add hl,de pop de .y_2: equ $+1: ld a,55: out (Y_PORT),a djnz .m2 ei push de .idfile1_3: equ $+1: ld a,#00 ld hl,#4000 .size_file_3: equ $+1: ld de,#4000 ld c,Dss.Read rst DssRst pop de ld hl,#4000 ld b,64; Ширина сегмента .y3: equ $+1: ld a,55: out (Y_PORT),a .m3: di ld l,l: ld a,(hl): ld a,a: ld (de),a: ld b,b: inc de push de .hght3: equ $+1: ld de,50: add hl,de pop de .y_3: equ $+1: ld a,55: out (Y_PORT),a djnz .m3 ei push de .idfile1_4: equ $+1: ld a,#00 ld hl,#4000 .size_file_4: equ $+1: ld de,#4000 ld c,Dss.Read rst DssRst pop de ld hl,#4000 ld b,64; Ширина сегмента .y4: equ $+1: ld a,55: out (Y_PORT),a .m4: di ld l,l: ld a,(hl): ld a,a: ld (de),a: ld b,b: inc de push de .hght4: equ $+1: ld de,50: add hl,de pop de .y_4: equ $+1: ld a,55: out (Y_PORT),a djnz .m4 ei push de .idfile1_5: equ $+1: ld a,#00 ld hl,#4000 .size_file_5: equ $+1: ld de,#4000 ld c,Dss.Read rst DssRst cp #ff jr nz,.skip ld (.exit_video),a .skip: pop de ld hl,#4000 ld b,64; Ширина сегмента .y5: equ $+1: ld a,55: out (Y_PORT),a .m5: di ld l,l: ld a,(hl): ld a,a: ld (de),a: ld b,b: inc de push de .hght5: equ $+1: ld de,50: add hl,de pop de .y_5: equ $+1: ld a,55: out (Y_PORT),a djnz .m5 ei ret .exit_video: db #00 ; HL = старшие 16 бит ; IX = младшие 16 бит Move_FP: ld a,(scr_id) .hl_reg: equ $+2:ld hl,#0000 .ix_reg: equ $+2: ld ix,0000 ld bc,#0015 rst DssRst ret ; ld a,file_id ; HL = старшие 16 бит ; IX = младшие 16 бит Move_FP_Small: ld bc,#0015 rst DssRst ret ; HL = старшие 16 бит ; IX = младшие 16 бит Move_FP_Sound: ld a,(snd_id) ld hl,(header.sound_off + 2) ld ix,(header.sound_off) ld bc,#0015 rst DssRst ret ; ============================================================ ; ПЕРЕКЛЮЧЕНИЕ ЭКРАННЫХ СТРАНИЦ ; ============================================================ Flip: ld hl,.flip_s .flip_s: equ $+1 ld a,#01 inc (hl) and %00000001 out (RGMOD),a cp 1 jr z,.skip ld hl,#c140 ld (DrawFrame.buffer_screen), hl ret .skip: ld hl,#c000 ld (DrawFrame.buffer_screen), hl ret ; ============================================================ ; НАСТРОЙКА COVOX-BLASTER ; ============================================================ SetUp_CovoxBL: ld bc,#04e ld a,%10011001 out (c),a ret ; ============================================================ ; НАСТРОЙКА ПРЕРЫВАНИЙ ; ============================================================ SetUp_Interrupt: DI LD HL,interrupt ld (#90ff),hl LD A,#90 LD I,A IM 2 EI ret ; ============================================================ ; ОБРАБОТЧИК ПРЕРЫВАНИЯ ; ============================================================ interrupt: di push af push bc push de push hl xor a in a,(#fe): and #80 call nz,next_data .next: pop hl pop de pop bc pop af ei ret .inter: db #00 next_data: in a,(#a2): ld (win1),a .trigger: equ $+1: ld a,#00 cp 0: jr nz,.n2 .page7: equ $+1: ld a,#00: out (#a2),a: jr .n3 .n2: .page6: equ $+1: ld a,#00: out (#a2),a .n3: ld hl,(addres_wav) ld c,#4f ld b,128 otir ld a,#80: cp h: jr nz,.next ld hl,#4000: ld (addres_wav),hl ld a,(.trigger) cpl: ld (.trigger),a .nn2: xor a: ld (sound_load),a .next: ld (addres_wav),hl ld a,(win1): out (#a2),a ret ; ============================================================ ; ОЧИСТКА ЗВУКА ; ============================================================ clear_covox: ld a,#80 ld bc,#4f clear_cbl: out (c),a djnz clear_cbl ret ; ============================================================ ; ОЧИСТКА ЭКРАНА ; ============================================================ clrscr: push bc push hl ld hl,(DrawFrame.buffer_screen) ld bc,#280 .m1: push bc di ld d,d: ld a,0: ld e,e .color: equ $+1: ld a,0: ld (hl),a ld b,b ei pop bc inc hl dec bc ld a,b: or c jr nz,.m1 pop hl pop bc ret ; ============================================================ ; УСТАНОВКА ПАЛИТРЫ ; ============================================================ ;hl addres palette SetPalette: ld de,#0000 ld bc,#ffa4 xor a rst BiosRst ret Copy_Palette: ld b,0 .sp_loop: push bc ld a,b out (Y_PORT),a ld hl,#c3e0 ld de,#c3e4 ld bc,#04 ldir pop bc djnz .sp_loop ret ; ============================================================ ; ОТКРЫТИЕ/ЗАКРЫТИЕ ФАЙЛА ; ============================================================ OpenFile: ld a,#C0 out (RGADR),a ld a,1 ld c,Dss.Open rst DssRst ld (idfile),a ret CloseFile: ld a,(idfile) ld c,Dss.Close rst DssRst ret ; ---- Загрузка заголовка ---- LoadHeader: ld hl,cmd call OpenFile ld a,(idfile) ld hl,header ld de,HEADER_SIZE ld c,Dss.Read rst DssRst call CloseFile ; ---- Установка параметров проигрывателя ---- ld a,(header.offset_y) ld (DrawFrame.y1),a: ld (DrawFrame.y_1),a ld (DrawFrame.y2),a: ld (DrawFrame.y_2),a ld (DrawFrame.y3),a: ld (DrawFrame.y_3),a ld (DrawFrame.y4),a: ld (DrawFrame.y_4),a ld (DrawFrame.y5),a: ld (DrawFrame.y_5),a ; ---- Устанавливаем высоту кадра ---- di ld d,d ld a,(header.height) ld b,b ei ld (DrawFrame.hght1),a ld (DrawFrame.hght2),a ld (DrawFrame.hght3),a ld (DrawFrame.hght4),a ld (DrawFrame.hght5),a ; ---- размер загружаемых получастей кадра ---- ld hl,(header.part_size) ld (DrawFrame.size_file_1),hl ld (DrawFrame.size_file_2),hl ld (DrawFrame.size_file_3),hl ld (DrawFrame.size_file_4),hl ld (DrawFrame.size_file_5),hl ld a,(header.mfp_pages) inc a ld (header.mfp_pages),a ret LoadMoveFP: .move_fp: equ $+1: ld a,#00 ld hl,#0000 ld ix,#0415 call Move_FP_Small .load: .page1: equ $+1: ld a,#00: out(#a2),a ld a,(header.mfp_pages) dec a ret z ld (header.mfp_pages), a ld a,(.move_fp) ld hl,#4000: ld d,h: ld e,l ld c,Dss.Read rst DssRst ld hl,#1000 ld (counter_notes),hl ret LoadBlockWav: .page6: equ $+1: ld a,#00: out (#a2),a .snd_id: equ $+1: ld a,#00 ld hl,#4000: ld d,h: ld e,l ld c,Dss.Read rst DssRst ld hl, (counter_notes) dec hl jr z,.skip ld (counter_notes),hl ret .skip: call LoadMoveFP.load jr Move_FP_Small_ LoadBlockWav_: .page7: equ $+1: ld a,#00: out (#a2),a .snd_id: equ $+1: ld a,#00 ld hl,#4000: ld d,h: ld e,l ld c,Dss.Read rst DssRst ld hl, (counter_notes) dec hl jr z,.skip ld (counter_notes),hl ret .skip: call LoadMoveFP.load jp Move_FP_Small_ Move_FP_Small_: .current_point: equ $+2: ld ix,(#4000) .current_point_2: equ $+1: ld hl,(#4002) ld a,(scr_id) ld bc,#0015 rst DssRst ld hl,(.current_point) inc hl: inc hl: inc hl: inc hl ld (.current_point),hl inc hl: inc hl ld (.current_point_2),hl ret ClearSound: ld a,(LoadBlockWav.page6): out (#a2),a xor a ld hl, #4000 ld de, #4001 ld bc, #3fff ld (hl), a ldir ld a,(LoadBlockWav_.page7): out (#a2),a xor a ld hl, #4000 ld de, #4001 ld bc, #3fff ld (hl), a ldir ret ; ============================================================ ; Облегчаем доступ к страницам памяти ; ============================================================ ExtractMemoryPage: ld b,10 ld a,(buffer) ld c,Dss.SetWin1 rst DssRst in a,(#a2): ld (DrawFrame.pageA),a ld b,5 ld a,(buffer) ld c,Dss.SetWin1 rst DssRst in a,(#a2): ld (next_data.page7),a: ld (LoadBlockWav_.page7),a ld b,4 ld a,(buffer) ld c,Dss.SetWin1 rst DssRst in a,(#a2): ld (next_data.page6),a: ld (LoadBlockWav.page6),a ld b,3 ld a,(buffer) ld c,Dss.SetWin1 rst DssRst in a,(#a2): ld (LoadMoveFP.page1),a ret ; ============================================================ ; КОНЕЦ ; ============================================================ code_end: savebin "player.exe",start_addr,code_end-start_addr