vplayer_for_sprinter/source/main3.asm
Константин Косарев 026dede6bc main
Попытка написать видеопроигрыватель для Sprinter computer
Есть еще над чем поработать.
2026-07-31 22:31:28 +03:00

855 lines
22 KiB
NASM
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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