360 lines
13 KiB
NASM
360 lines
13 KiB
NASM
; moe-bius - zx/tsfm player
|
|
; --atemka 14.o8.2o25
|
|
|
|
device zxspectrum128
|
|
|
|
ivt equ 0x8000
|
|
isr equ 0x8181
|
|
codestart equ 0x8080
|
|
|
|
reg_buffer equ 0x5B00 ; basic variables :meatjob:
|
|
|
|
EMPTYTAP "player.tap"
|
|
|
|
; IM2 vector table - all 257 values for floating bus condition
|
|
org ivt
|
|
block 257, (isr >> 8) ; isr at 0x8181
|
|
|
|
; dummy ISR (vblank synchronization is done by HALT)
|
|
org isr
|
|
stack_top:
|
|
ei
|
|
ret
|
|
|
|
; entrypoint
|
|
start:
|
|
di
|
|
ld sp, stack_top
|
|
|
|
; set IM2 vector table to 0x80
|
|
ld a, high ivt
|
|
ld i, a
|
|
im 2
|
|
|
|
; prefill register buffer to prevent crashes
|
|
ld hl, reg_buffer
|
|
ldi [hl], 0x2F : ldi [hl], 0xFF
|
|
ldi [hl], 0x2D : ldi [hl], 0xFF
|
|
ldi [hl], -1 : ldi [hl], -1
|
|
|
|
; enable interrupts
|
|
ei
|
|
.loop:
|
|
halt
|
|
ld a, 1 : out (0xfe), a
|
|
ld bc, 0xFFFD
|
|
ld de, 0x00C0
|
|
ld a, 0b11111000
|
|
out (c), a
|
|
ld hl, reg_buffer
|
|
call player.reg_out
|
|
ld a, 1 : out (0xfe), a
|
|
ld a, 0b11111001
|
|
ld b, 0xFF
|
|
out (c), a
|
|
inc hl
|
|
call player.reg_out
|
|
ld a, 1 : out (0xfe), a
|
|
|
|
; draw vu meters
|
|
ld bc, 0x4000+2048*2+32*7+0
|
|
call vumeter.draw_0
|
|
.vu0 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+1
|
|
call vumeter.draw_0
|
|
.vu1 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+2
|
|
call vumeter.draw_0
|
|
.vu2 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+3
|
|
call vumeter.draw_0
|
|
.vu3 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+4
|
|
call vumeter.draw_0
|
|
.vu4 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+5
|
|
call vumeter.draw_0
|
|
.vu5 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+6
|
|
call vumeter.draw_0
|
|
.vu6 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+7
|
|
call vumeter.draw_0
|
|
.vu7 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+8
|
|
call vumeter.draw_0
|
|
.vu8 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+9
|
|
call vumeter.draw_0
|
|
.vu9 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+10
|
|
call vumeter.draw_0
|
|
.vu10 equ $-2
|
|
ld bc, 0x4000+2048*2+32*7+11
|
|
call vumeter.draw_0
|
|
.vu11 equ $-2
|
|
|
|
ld iy, reg_buffer
|
|
call player.play_tick
|
|
ld a, 1 : out (0xfe), a
|
|
call vumeter.init_frame
|
|
xor a : out (0xfe), a
|
|
|
|
; and jump to next frame
|
|
xor a : ld bc, 0x7FFD : out (c), a ; map fast page to fix 128k AY ports contention
|
|
jp .loop
|
|
|
|
; ----------------------------------------------------
|
|
; vu meters
|
|
; orig by natt, rewritten by me
|
|
if 1
|
|
vumeter:
|
|
.init_frame:
|
|
ld a, [player.player_channels+(player.channel_struct_t*1)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu0], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*2)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu1], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*3)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu2], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*4)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu3], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*5)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu4], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*6)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu5], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*9)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu6], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*10)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu7], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*11)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu8], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*12)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu9], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*13)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu10], hl
|
|
ld a, [player.player_channels+(player.channel_struct_t*14)+player.channel_struct_t.volume]
|
|
call .get_address
|
|
ld [start.vu11], hl
|
|
|
|
ret
|
|
|
|
; DE - screen pos, A - register value
|
|
.get_address:
|
|
bit 4, a
|
|
jp nz, .get_e
|
|
and 0x0E
|
|
ld h, high .calltab
|
|
ld l, a
|
|
ld e, [hl] : inc l
|
|
ld d, [hl]
|
|
exd
|
|
ret
|
|
.get_e:
|
|
ld hl, .draw_e
|
|
ret
|
|
|
|
; ------------------
|
|
; drawing routines
|
|
; BC - dst
|
|
.draw_e:
|
|
ld a, 0b00000000 : ld [bc], a : inc b ; 18
|
|
ld a, 0b11111110 : ld [bc], a : inc b ; 18
|
|
ld a, 0b11000110 : ld [bc], a : inc b ; 18
|
|
ld a, 0b11011110 : ld [bc], a : inc b ; 18
|
|
ld a, 0b11001110 : ld [bc], a : inc b ; 18
|
|
ld a, 0b11011110 : ld [bc], a : inc b ; 18
|
|
ld a, 0b11000110 : ld [bc], a : inc b ; 18
|
|
ld a, 0b11111110 : ld [bc], a : ret ; 24 = 150t per char
|
|
|
|
.draw_0:
|
|
dup 7 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*7
|
|
dup 1 : ld a, 0x00 : ld [bc], a : ret : edup ; 24*1
|
|
.draw_1:
|
|
dup 6 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*6
|
|
dup 1 : ld a, 0xFE : ld [bc], a : inc b : edup ; 18*1
|
|
dup 1 : ld a, 0xFE : ld [bc], a : ret : edup ; 24
|
|
.draw_2:
|
|
dup 5 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*5
|
|
dup 2 : ld a, 0xFE : ld [bc], a : inc b : edup ; 18*2
|
|
dup 1 : ld a, 0xFE : ld [bc], a : ret : edup ; 24
|
|
.draw_3:
|
|
dup 4 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*4
|
|
dup 3 : ld a, 0xFE : ld [bc], a : inc b : edup ; 18*3
|
|
dup 1 : ld a, 0xFE : ld [bc], a : ret : edup ; 24
|
|
.draw_4:
|
|
dup 3 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*3
|
|
dup 4 : ld a, 0xFE : ld [bc], a : inc b : edup ; 18*4
|
|
dup 1 : ld a, 0xFE : ld [bc], a : ret : edup ; 24
|
|
.draw_5:
|
|
dup 2 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*2
|
|
dup 5 : ld a, 0xFE : ld [bc], a : inc b : edup ; 18*5
|
|
dup 1 : ld a, 0xFE : ld [bc], a : ret : edup ; 24
|
|
.draw_6:
|
|
dup 1 : ld a, 0x00 : ld [bc], a : inc b : edup ; 18*1
|
|
dup 6 : ld a, 0xFE : ld [bc], a : inc b : edup ; 18*6
|
|
dup 1 : ld a, 0xFE : ld [bc], a : ret : edup ; 24
|
|
.draw_7:
|
|
dup 7 : ld a, 0xFE : ld [bc], a : inc b : edup ; 18*6
|
|
dup 1 : ld a, 0xFE : ld [bc], a : ret : edup ; 24
|
|
|
|
align 256
|
|
.calltab:
|
|
dw .draw_0, .draw_1, .draw_2, .draw_3, .draw_4, .draw_5, .draw_6, .draw_7
|
|
endif
|
|
|
|
; player include
|
|
include "player.asm"
|
|
|
|
; music
|
|
|
|
|
|
; not so hidden text
|
|
db "..........................", 10
|
|
db "hey there again!", 10
|
|
db "another arson? another arson!", 10
|
|
db "this time for the ZX Spectrum 128 + TurboSound FM! (2 x YM2203)", 10
|
|
db 10
|
|
db "player code - artemka", 10
|
|
db "splash screen - Rui8bit and Grongy", 10
|
|
db "music - Abstract 64, Alex Winston, Natt, nIk0, Pator and Rei8bit", 10
|
|
db "support and tools - AArt1526", 10
|
|
db 10
|
|
db "powered by Furnace Tracker =)", 10
|
|
db 10
|
|
db "released at Xenium 2o25", 10
|
|
db 10
|
|
db "greets to everyone pushing the limits and keeping the scene alive! (and whatever ;)", 10
|
|
db "special thanks to CHRV for the TSFMPro board :)", 10
|
|
db 10
|
|
db 10
|
|
db 10
|
|
db "oh yea, we broke all your emulators!", 10
|
|
db "unlike regular TS, the TSFM wasn't common at the speccy scene (its peak was when the device was", 10
|
|
db "released, that is, mid-late 2000s), and most of features like CSM were not explored much", 10
|
|
db "so, if you're going to fix your emulator, follow these rules: ", 10
|
|
db " - use a decent FM emulation core! ymfm and Nuked-OPN2 are the best ones", 10
|
|
db " - make sure you have implemented extended channel 3, SSG-EG and CSM timer functionality properly", 10
|
|
db " - failure to do so will result in detuned chords and broken vocal chops at the start", 10
|
|
db " - match the FM and AY/SSG mixing levels so single FM channel with square-like instrument at max volume", 10
|
|
db " has the same amplitude as the SSG channel at max volume", 10
|
|
db " (this seems to correspond to equal FM and SSG mixing levels in Chip Manager in Furnace)", 10
|
|
db "good luck! ;)", 10
|
|
db 10
|
|
db "--artemka 21.08.2025", 10
|
|
db 10
|
|
db 10
|
|
db 10
|
|
db 10
|
|
db 10
|
|
db "p.s. try this choon at 60 Hz VBlank rate - it sounds **wonderful** :D", 10
|
|
db 10
|
|
db 10
|
|
db 10
|
|
db 10
|
|
db 10
|
|
db 10
|
|
db "p.p.s. come to Multimatograf 2026 - the party that cares about the demoscene - spring 2026 - events.retroscene.org", 10
|
|
|
|
music_p0_ch1:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch1.bin"
|
|
music_p0_ch3:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch3.bin"
|
|
|
|
code_end
|
|
|
|
; -------------------------------------------------
|
|
; music pages
|
|
page 0
|
|
emptytap "page0.tap"
|
|
org 0xC000
|
|
music_p0_ch12:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch12.bin"
|
|
music_p0_ch13:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch13.bin"
|
|
savetap "page0.tap",CODE,"page0",0xC000,$-0xC000
|
|
|
|
; ------------------------------------------------
|
|
page 1
|
|
emptytap "page1.tap"
|
|
org 0xC000
|
|
music_p0_ch14:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch14.bin"
|
|
music_p0_ch7:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch7.bin"
|
|
savetap "page1.tap",CODE,"page1",0xC000,$-0xC000
|
|
|
|
; ------------------------------------------------
|
|
page 3
|
|
emptytap "page3.tap"
|
|
org 0xC000
|
|
music_p0_ch10:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch10.bin"
|
|
music_p0_ch9:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch9.bin"
|
|
savetap "page3.tap",CODE,"page3",0xC000,$-0xC000
|
|
|
|
; ------------------------------------------------
|
|
page 4
|
|
emptytap "page4.tap"
|
|
org 0xC000
|
|
music_p0_ch2:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch2.bin"
|
|
music_p0_ch0:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch0.bin"
|
|
savetap "page4.tap",CODE,"page4",0xC000,$-0xC000
|
|
|
|
; ------------------------------------------------
|
|
page 6
|
|
emptytap "page6.tap"
|
|
org 0xC000
|
|
music_p0_ch11:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch11.bin"
|
|
music_p0_ch8:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch8.bin"
|
|
savetap "page6.tap",CODE,"page6",0xC000,$-0xC000
|
|
|
|
; ------------------------------------------------
|
|
page 7
|
|
emptytap "page7.tap"
|
|
org 0xC000
|
|
music_p0_ch4:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch4.bin"
|
|
music_p0_ch6:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch6.bin"
|
|
music_p0_ch5:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch5.bin"
|
|
music_p0_ch15:
|
|
incbin "tsfm_done_optimized/tsfm_done_optimized_ch15.bin"
|
|
savetap "page7.tap",CODE,"page7",0xC000,$-0xC000
|
|
|
|
; splash screen
|
|
emptytap "splash.tap"
|
|
org 0x4000
|
|
splash:
|
|
;block 6144, 0
|
|
;block 768, (0<<3)|(7<<0)
|
|
;incbin "!gfx/tsfm2.scr"
|
|
incbin "!gfx/tsfm_fin3.scr"
|
|
savetap "splash.tap",CODE,"splash",0x4000,$-0x4000
|
|
|
|
endmodule
|
|
; ----------------------------------------------------
|
|
total_codelength equ code_end-codestart
|
|
|
|
display "total: ", /a, total_codelength, " bytes"
|
|
|
|
savesna "moebius.sna", start
|
|
savetap "player.tap",CODE,"moebius",codestart,total_codelength
|
|
|