mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-02 12:41:30 +00:00
sms: redid climber; added PSGLib
This commit is contained in:
parent
138ee806cb
commit
5b39cd51ed
|
@ -174,6 +174,7 @@ TODO:
|
|||
- list of stuff for policy
|
||||
- popup
|
||||
- convert binary to hex stmts
|
||||
- "suggestions" (vblank overrun, variable # scanlines, etc)
|
||||
|
||||
|
||||
WEB WORKER FORMAT
|
||||
|
@ -319,3 +320,15 @@ Converting from NESASM to DASM
|
|||
- .db to .byte, .dw to .word
|
||||
- use NES_HEADER macros
|
||||
- no .bank
|
||||
|
||||
Cross platform NES/SMS/GG library
|
||||
- use CHR RAM
|
||||
- create flipped tiles/sprites
|
||||
- create alternate palette tiles
|
||||
- metatiles
|
||||
- cross platform music/sound
|
||||
- scrolling
|
||||
- row/column mask
|
||||
- no nametable mirroring in SMS
|
||||
- 256x240 vs 256x192
|
||||
|
||||
|
|
556
presets/sms-sms-libcv/PSGlib.c
Normal file
556
presets/sms-sms-libcv/PSGlib.c
Normal file
|
@ -0,0 +1,556 @@
|
|||
/* **************************************************
|
||||
PSGlib - C programming library for the SEGA PSG
|
||||
( part of devkitSMS - github.com/sverx/devkitSMS )
|
||||
************************************************** */
|
||||
|
||||
// http://www.smspower.org/forums/16925-PSGToolAVGMToPSGConvertor
|
||||
// https://github.com/sverx/PSGlib/blob/master/tools/src/vgm2psg.c
|
||||
// http://steveproxna.blogspot.com/2017/11/devkitsms-programming-sample.html
|
||||
// http://battleofthebits.org/lyceum/View/Vortex+Tracker+II/
|
||||
|
||||
#include "PSGlib.h"
|
||||
|
||||
#define PSGDataPort #0x7f
|
||||
|
||||
#define PSGLatch #0x80
|
||||
#define PSGData #0x40
|
||||
|
||||
#define PSGChannel0 #0b00000000
|
||||
#define PSGChannel1 #0b00100000
|
||||
#define PSGChannel2 #0b01000000
|
||||
#define PSGChannel3 #0b01100000
|
||||
#define PSGVolumeData #0b00010000
|
||||
|
||||
#define PSGWait #0x38
|
||||
#define PSGSubString #0x08
|
||||
#define PSGLoop #0x01
|
||||
#define PSGEnd #0x00
|
||||
|
||||
/* define PSGPort (SDCC z80 syntax) */
|
||||
__sfr __at 0x7F PSGPort;
|
||||
|
||||
// fundamental vars
|
||||
unsigned char PSGMusicStatus; // are we playing a background music?
|
||||
void *PSGMusicStart; // the pointer to the beginning of music
|
||||
void *PSGMusicPointer; // the pointer to the current
|
||||
void *PSGMusicLoopPoint; // the pointer to the loop begin
|
||||
unsigned char PSGMusicSkipFrames; // the frames we need to skip
|
||||
unsigned char PSGLoopFlag; // the tune should loop or not (flag)
|
||||
unsigned char PSGMusicLastLatch; // the last PSG music latch
|
||||
unsigned char PSGMusicVolumeAttenuation; // the volume attenuation applied to the tune (0-15)
|
||||
|
||||
// decompression vars
|
||||
unsigned char PSGMusicSubstringLen; // lenght of the substring we are playing
|
||||
void *PSGMusicSubstringRetAddr; // return to this address when substring is over
|
||||
|
||||
// volume/frequence buffering
|
||||
unsigned char PSGChan0Volume; // the volume for channel 0
|
||||
unsigned char PSGChan1Volume; // the volume for channel 1
|
||||
unsigned char PSGChan2Volume; // the volume for channel 2
|
||||
unsigned char PSGChan3Volume; // the volume for channel 3
|
||||
unsigned char PSGChan2LowTone; // the low tone bits for channels 2
|
||||
unsigned char PSGChan2HighTone; // the high tone bits for channel 2
|
||||
unsigned char PSGChan3LowTone; // the tone bits for channels 3
|
||||
|
||||
// flags for channels 2-3 access
|
||||
unsigned char PSGChannel2SFX; // !0 means channel 2 is allocated to SFX
|
||||
unsigned char PSGChannel3SFX; // !0 means channel 3 is allocated to SFX
|
||||
|
||||
// volume/frequence buffering for SFX
|
||||
unsigned char PSGSFXChan2Volume; // the volume for SFX channel 2
|
||||
unsigned char PSGSFXChan3Volume; // the volume for SFX channel 3
|
||||
|
||||
// fundamental vars for SFX
|
||||
unsigned char PSGSFXStatus; // are we playing a SFX?
|
||||
void *PSGSFXStart; // the pointer to the beginning of SFX
|
||||
void *PSGSFXPointer; // the pointer to the current address
|
||||
void *PSGSFXLoopPoint; // the pointer to the loop begin
|
||||
unsigned char PSGSFXSkipFrames; // the frames we need to skip
|
||||
unsigned char PSGSFXLoopFlag; // the SFX should loop or not (flag)
|
||||
|
||||
// decompression vars for SFX
|
||||
unsigned char PSGSFXSubstringLen; // lenght of the substring we are playing
|
||||
void *PSGSFXSubstringRetAddr; // return to this address when substring is over
|
||||
|
||||
void PSGStop (void) {
|
||||
/* *********************************************************************
|
||||
stops the music (leaving the SFX on, if it's playing)
|
||||
*/
|
||||
if (PSGMusicStatus) {
|
||||
PSGPort=PSGLatch|PSGChannel0|PSGVolumeData|0x0F; // latch channel 0, volume=0xF (silent)
|
||||
PSGPort=PSGLatch|PSGChannel1|PSGVolumeData|0x0F; // latch channel 1, volume=0xF (silent)
|
||||
if (!PSGChannel2SFX)
|
||||
PSGPort=PSGLatch|PSGChannel2|PSGVolumeData|0x0F; // latch channel 2, volume=0xF (silent)
|
||||
if (!PSGChannel3SFX)
|
||||
PSGPort=PSGLatch|PSGChannel3|PSGVolumeData|0x0F; // latch channel 3, volume=0xF (silent)
|
||||
PSGMusicStatus=PSG_STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
void PSGResume (void) {
|
||||
/* *********************************************************************
|
||||
resume the previously playing music
|
||||
*/
|
||||
if (!PSGMusicStatus) {
|
||||
PSGPort=PSGLatch|PSGChannel0|PSGVolumeData|PSGChan0Volume; // restore channel 0 volume
|
||||
PSGPort=PSGLatch|PSGChannel1|PSGVolumeData|PSGChan1Volume; // restore channel 1 volume
|
||||
if (!PSGChannel2SFX) {
|
||||
PSGPort=PSGLatch|PSGChannel2|(PSGChan2LowTone&0x0F); // restore channel 2 frequency
|
||||
PSGPort=PSGChan2HighTone&0x3F;
|
||||
PSGPort=PSGLatch|PSGChannel2|PSGVolumeData|PSGChan2Volume; // restore channel 2 volume
|
||||
}
|
||||
if (!PSGChannel3SFX) {
|
||||
PSGPort=PSGLatch|PSGChannel3|(PSGChan3LowTone&0x0F); // restore channel 3 frequency
|
||||
PSGPort=PSGLatch|PSGChannel3|PSGVolumeData|PSGChan3Volume; // restore channel 3 volume
|
||||
}
|
||||
PSGMusicStatus=PSG_PLAYING;
|
||||
}
|
||||
}
|
||||
|
||||
void PSGPlay (void *song) {
|
||||
/* *********************************************************************
|
||||
receives the address of the PSG to start playing (continuously)
|
||||
*/
|
||||
PSGStop();
|
||||
PSGLoopFlag=1;
|
||||
PSGMusicStart=song; // store the begin point of music
|
||||
PSGMusicPointer=song; // set music pointer to begin of music
|
||||
PSGMusicLoopPoint=song; // looppointer points to begin too
|
||||
|
||||
PSGMusicSkipFrames=0; // reset the skip frames
|
||||
PSGMusicSubstringLen=0; // reset the substring len (for compression)
|
||||
PSGMusicLastLatch=PSGLatch|PSGChannel0|PSGVolumeData|0x0F; // latch channel 0, volume=0xF (silent)
|
||||
PSGMusicStatus=PSG_PLAYING;
|
||||
}
|
||||
|
||||
void PSGCancelLoop (void) {
|
||||
/* *********************************************************************
|
||||
sets the currently looping music to no more loops after the current
|
||||
*/
|
||||
PSGLoopFlag=0;
|
||||
}
|
||||
|
||||
void PSGPlayNoRepeat (void *song) {
|
||||
/* *********************************************************************
|
||||
receives the address of the PSG to start playing (once)
|
||||
*/
|
||||
PSGPlay(song);
|
||||
PSGLoopFlag=0;
|
||||
}
|
||||
|
||||
unsigned char PSGGetStatus (void) {
|
||||
/* *********************************************************************
|
||||
returns the current status of music
|
||||
*/
|
||||
return(PSGMusicStatus);
|
||||
}
|
||||
|
||||
void PSGSilenceChannels (void) {
|
||||
/* *********************************************************************
|
||||
silence all the PSG channels
|
||||
*/
|
||||
PSGPort=PSGLatch|PSGChannel0|PSGVolumeData|0x0F;
|
||||
PSGPort=PSGLatch|PSGChannel1|PSGVolumeData|0x0F;
|
||||
PSGPort=PSGLatch|PSGChannel2|PSGVolumeData|0x0F;
|
||||
PSGPort=PSGLatch|PSGChannel3|PSGVolumeData|0x0F;
|
||||
}
|
||||
|
||||
void PSGRestoreVolumes (void) {
|
||||
/* *********************************************************************
|
||||
restore the PSG channels volumes (if a tune or an SFX uses them!)
|
||||
*/
|
||||
if (PSGMusicStatus) {
|
||||
PSGPort=PSGLatch|PSGChannel0|PSGVolumeData|((PSGChan0Volume+PSGMusicVolumeAttenuation>15)?15:PSGChan0Volume+PSGMusicVolumeAttenuation);
|
||||
PSGPort=PSGLatch|PSGChannel1|PSGVolumeData|((PSGChan1Volume+PSGMusicVolumeAttenuation>15)?15:PSGChan1Volume+PSGMusicVolumeAttenuation);
|
||||
}
|
||||
if (PSGChannel2SFX)
|
||||
PSGPort=PSGLatch|PSGChannel2|PSGVolumeData|PSGSFXChan2Volume;
|
||||
else if (PSGMusicStatus)
|
||||
PSGPort=PSGLatch|PSGChannel2|PSGVolumeData|((PSGChan2Volume+PSGMusicVolumeAttenuation>15)?15:PSGChan2Volume+PSGMusicVolumeAttenuation);
|
||||
if (PSGChannel3SFX)
|
||||
PSGPort=PSGLatch|PSGChannel3|PSGVolumeData|PSGSFXChan3Volume;
|
||||
else if (PSGMusicStatus)
|
||||
PSGPort=PSGLatch|PSGChannel3|PSGVolumeData|((PSGChan3Volume+PSGMusicVolumeAttenuation>15)?15:PSGChan3Volume+PSGMusicVolumeAttenuation);
|
||||
}
|
||||
|
||||
void PSGSetMusicVolumeAttenuation (unsigned char attenuation) {
|
||||
/* *********************************************************************
|
||||
sets the volume attenuation for the music (0-15)
|
||||
*/
|
||||
PSGMusicVolumeAttenuation=attenuation;
|
||||
if (PSGMusicStatus) {
|
||||
PSGPort=PSGLatch|PSGChannel0|PSGVolumeData|((PSGChan0Volume+PSGMusicVolumeAttenuation>15)?15:PSGChan0Volume+PSGMusicVolumeAttenuation);
|
||||
PSGPort=PSGLatch|PSGChannel1|PSGVolumeData|((PSGChan1Volume+PSGMusicVolumeAttenuation>15)?15:PSGChan1Volume+PSGMusicVolumeAttenuation);
|
||||
if (!PSGChannel2SFX)
|
||||
PSGPort=PSGLatch|PSGChannel2|PSGVolumeData|((PSGChan2Volume+PSGMusicVolumeAttenuation>15)?15:PSGChan2Volume+PSGMusicVolumeAttenuation);
|
||||
if (!PSGChannel3SFX)
|
||||
PSGPort=PSGLatch|PSGChannel3|PSGVolumeData|((PSGChan3Volume+PSGMusicVolumeAttenuation>15)?15:PSGChan3Volume+PSGMusicVolumeAttenuation);
|
||||
}
|
||||
}
|
||||
|
||||
void PSGSFXStop (void) {
|
||||
/* *********************************************************************
|
||||
stops the SFX (leaving the music on, if it's playing)
|
||||
*/
|
||||
if (PSGSFXStatus) {
|
||||
if (PSGChannel2SFX) {
|
||||
if (PSGMusicStatus) {
|
||||
PSGPort=PSGLatch|PSGChannel2|(PSGChan2LowTone&0x0F);
|
||||
PSGPort=PSGChan2HighTone&0x3F;
|
||||
PSGPort=PSGLatch|PSGChannel2|PSGVolumeData|(((PSGChan2Volume&0x0F)+PSGMusicVolumeAttenuation>15)?15:(PSGChan2Volume&0x0F)+PSGMusicVolumeAttenuation);
|
||||
} else {
|
||||
PSGPort=PSGLatch|PSGChannel2|PSGVolumeData|0x0F;
|
||||
}
|
||||
PSGChannel2SFX=PSG_STOPPED;
|
||||
}
|
||||
|
||||
if (PSGChannel3SFX) {
|
||||
if (PSGMusicStatus) {
|
||||
PSGPort=PSGLatch|PSGChannel3|(PSGChan3LowTone&0x0F);
|
||||
PSGPort=PSGLatch|PSGChannel3|PSGVolumeData|(((PSGChan3Volume&0x0F)+PSGMusicVolumeAttenuation>15)?15:(PSGChan3Volume&0x0F)+PSGMusicVolumeAttenuation);
|
||||
} else {
|
||||
PSGPort=PSGLatch|PSGChannel3|PSGVolumeData|0x0F;
|
||||
}
|
||||
PSGChannel3SFX=PSG_STOPPED;
|
||||
}
|
||||
PSGSFXStatus=PSG_STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
void PSGSFXPlay (void *sfx, unsigned char channels) {
|
||||
/* *********************************************************************
|
||||
receives the address of the SFX to start and the mask that indicates
|
||||
which channel(s) the SFX will use
|
||||
*/
|
||||
PSGSFXStop();
|
||||
PSGSFXLoopFlag=0;
|
||||
PSGSFXStart=sfx; // store begin of SFX
|
||||
PSGSFXPointer=sfx; // set the pointer to begin of SFX
|
||||
PSGSFXLoopPoint=sfx; // looppointer points to begin too
|
||||
PSGSFXSkipFrames=0; // reset the skip frames
|
||||
PSGSFXSubstringLen=0; // reset the substring len
|
||||
PSGChannel2SFX=(channels&SFX_CHANNEL2)?PSG_PLAYING:PSG_STOPPED;
|
||||
PSGChannel3SFX=(channels&SFX_CHANNEL3)?PSG_PLAYING:PSG_STOPPED;
|
||||
PSGSFXStatus=PSG_PLAYING;
|
||||
}
|
||||
|
||||
void PSGSFXCancelLoop (void) {
|
||||
/* *********************************************************************
|
||||
sets the currently looping SFX to no more loops after the current
|
||||
*/
|
||||
PSGSFXLoopFlag=0;
|
||||
}
|
||||
|
||||
unsigned char PSGSFXGetStatus (void) {
|
||||
/* *********************************************************************
|
||||
returns the current SFX status
|
||||
*/
|
||||
return(PSGSFXStatus);
|
||||
}
|
||||
|
||||
void PSGSFXPlayLoop (void *sfx, unsigned char channels) {
|
||||
/* *********************************************************************
|
||||
receives the address of the SFX to start continuously and the mask
|
||||
that indicates which channel(s) the SFX will use
|
||||
*/
|
||||
PSGSFXPlay(sfx, channels);
|
||||
PSGSFXLoopFlag=1;
|
||||
}
|
||||
|
||||
void PSGFrame (void) {
|
||||
/* *********************************************************************
|
||||
processes a music frame
|
||||
*/
|
||||
__asm
|
||||
ld a,(_PSGMusicStatus) ; check if we have got to play a tune
|
||||
or a
|
||||
ret z
|
||||
|
||||
ld a,(_PSGMusicSkipFrames) ; check if we have got to skip frames
|
||||
or a
|
||||
jp nz,_skipFrame
|
||||
|
||||
ld hl,(_PSGMusicPointer) ; read current address
|
||||
|
||||
_intLoop:
|
||||
ld b,(hl) ; load PSG byte (in B)
|
||||
inc hl ; point to next byte
|
||||
ld a,(_PSGMusicSubstringLen) ; read substring len
|
||||
or a
|
||||
jr z,_continue ; check if it is 0 (we are not in a substring)
|
||||
dec a ; decrease len
|
||||
ld (_PSGMusicSubstringLen),a ; save len
|
||||
jr nz,_continue
|
||||
ld hl,(_PSGMusicSubstringRetAddr) ; substring is over, retrieve return address
|
||||
|
||||
_continue:
|
||||
ld a,b ; copy PSG byte into A
|
||||
cp PSGLatch ; is it a latch?
|
||||
jr c,_noLatch ; if < $80 then it is NOT a latch
|
||||
ld (_PSGMusicLastLatch),a ; it is a latch - save it in "LastLatch"
|
||||
|
||||
; we have got the latch PSG byte both in A and in B
|
||||
; and we have to check if the value should pass to PSG or not
|
||||
bit 4,a ; test if it is a volume
|
||||
jr nz,_latch_Volume ; jump if volume data
|
||||
bit 6,a ; test if the latch it is for channels 0-1 or for 2-3
|
||||
jp z,_send2PSG_A ; send data to PSG if it is for channels 0-1
|
||||
|
||||
; we have got the latch (tone, chn 2 or 3) PSG byte both in A and in B
|
||||
; and we have to check if the value should be passed to PSG or not
|
||||
bit 5,a ; test if tone it is for channel 2 or 3
|
||||
jr z,_ifchn2 ; jump if channel 2
|
||||
ld (_PSGChan3LowTone),a ; save tone LOW data
|
||||
ld a,(_PSGChannel3SFX) ; channel 3 free?
|
||||
or a
|
||||
jp nz,_intLoop
|
||||
ld a,(_PSGChan3LowTone)
|
||||
and #3 ; test if channel 3 is set to use the frequency of channel 2
|
||||
cp #3
|
||||
jr nz,_send2PSG_B ; if channel 3 does not use frequency of channel 2 jump
|
||||
ld a,(_PSGSFXStatus) ; test if an SFX is playing
|
||||
or a
|
||||
jr z,_send2PSG_B ; if no SFX is playing jump
|
||||
ld (_PSGChannel3SFX),a ; otherwise mark channel 3 as occupied
|
||||
ld a,PSGLatch|PSGChannel3|PSGVolumeData|#0x0F ; and silence channel 3
|
||||
out (PSGDataPort),a
|
||||
jp _intLoop
|
||||
_ifchn2:
|
||||
ld (_PSGChan2LowTone),a ; save tone LOW data
|
||||
ld a,(_PSGChannel2SFX) ; channel 2 free?
|
||||
or a
|
||||
jr z,_send2PSG_B
|
||||
jp _intLoop
|
||||
|
||||
_latch_Volume:
|
||||
bit 6,a ; test if the latch it is for channels 0-1 or for 2-3
|
||||
jr nz,_latch_Volume_23 ; volume is for channel 2 or 3
|
||||
bit 5,a ; test if volume it is for channel 0 or 1
|
||||
jr z,_chn0 ; jump for channel 0
|
||||
ld (_PSGChan1Volume),a ; save volume data
|
||||
jp _sendVolume2PSG_A
|
||||
_chn0:
|
||||
ld (_PSGChan0Volume),a ; save volume data
|
||||
jp _sendVolume2PSG_A
|
||||
|
||||
_latch_Volume_23:
|
||||
bit 5,a ; test if volume it is for channel 2 or 3
|
||||
jr z,_chn2 ; jump for channel 2
|
||||
ld (_PSGChan3Volume),a ; save volume data
|
||||
ld a,(_PSGChannel3SFX) ; channel 3 free?
|
||||
or a
|
||||
jr z,_sendVolume2PSG_B
|
||||
jp _intLoop
|
||||
_chn2:
|
||||
ld (_PSGChan2Volume),a ; save volume data
|
||||
ld a,(_PSGChannel2SFX) ; channel 2 free?
|
||||
or a
|
||||
jr z,_sendVolume2PSG_B
|
||||
jp _intLoop
|
||||
|
||||
_skipFrame:
|
||||
dec a
|
||||
ld (_PSGMusicSkipFrames),a
|
||||
ret
|
||||
|
||||
_noLatch:
|
||||
cp PSGData
|
||||
jr c,_command ; if < $40 then it is a command
|
||||
; it is a data
|
||||
ld a,(_PSGMusicLastLatch) ; retrieve last latch
|
||||
jp _output_NoLatch
|
||||
|
||||
_command:
|
||||
cp PSGWait
|
||||
jr z,_done ; no additional frames
|
||||
jr c,_otherCommands ; other commands?
|
||||
and #0x07 ; take only the last 3 bits for skip frames
|
||||
ld (_PSGMusicSkipFrames),a ; we got additional frames
|
||||
_done:
|
||||
ld (_PSGMusicPointer),hl ; save current address
|
||||
ret ; frame done
|
||||
|
||||
_otherCommands:
|
||||
cp PSGSubString
|
||||
jr nc,_substring
|
||||
cp PSGEnd
|
||||
jr z,_musicLoop
|
||||
cp PSGLoop
|
||||
jr z,_setLoopPoint
|
||||
|
||||
; ***************************************************************************
|
||||
; we should never get here!
|
||||
; if we do, it means the PSG file is probably corrupted, so we just RET
|
||||
; ***************************************************************************
|
||||
|
||||
ret
|
||||
|
||||
_send2PSG_B:
|
||||
ld a,b
|
||||
_send2PSG_A:
|
||||
out (PSGDataPort),a ; output the byte
|
||||
jp _intLoop
|
||||
|
||||
_sendVolume2PSG_B:
|
||||
ld a,b
|
||||
_sendVolume2PSG_A:
|
||||
ld c,a ; save the PSG command byte
|
||||
and #0x0F ; keep lower nibble
|
||||
ld b,a ; save value
|
||||
ld a,(_PSGMusicVolumeAttenuation) ; load volume attenuation
|
||||
add a,b ; add value
|
||||
cp #0x0F ; check overflow
|
||||
jr c,_no_overflow ; if it is <=15 then ok
|
||||
ld a,#0x0F ; else, reset to 15
|
||||
_no_overflow:
|
||||
ld b,a ; save new attenuated volume value
|
||||
ld a,c ; retrieve PSG command
|
||||
and #0xF0 ; keep upper nibble
|
||||
or b ; set attenuated volume
|
||||
out (PSGDataPort),a ; output the byte
|
||||
jp _intLoop
|
||||
|
||||
_output_NoLatch:
|
||||
; we got the last latch in A and the PSG data in B
|
||||
; and we have to check if the value should pass to PSG or not
|
||||
; note that non-latch commands can be only contain frequencies (no volumes)
|
||||
; for channels 0,1,2 only (no noise)
|
||||
bit 6,a ; test if the latch it is for channels 0-1 or for chn 2
|
||||
jr nz,_high_part_Tone ; it is tone data for channel 2
|
||||
jp _send2PSG_B ; otherwise, it is for chn 0 or 1 so we have done!
|
||||
|
||||
_setLoopPoint:
|
||||
ld (_PSGMusicLoopPoint),hl
|
||||
jp _intLoop
|
||||
|
||||
_musicLoop:
|
||||
ld a,(_PSGLoopFlag) ; looping requested?
|
||||
or a
|
||||
jp z,_PSGStop ; No:stop it! (tail call optimization)
|
||||
ld hl,(_PSGMusicLoopPoint)
|
||||
jp _intLoop
|
||||
|
||||
_substring:
|
||||
sub PSGSubString-4 ; len is value - $08 + 4
|
||||
ld (_PSGMusicSubstringLen),a ; save len
|
||||
ld c,(hl) ; load substring address (offset)
|
||||
inc hl
|
||||
ld b,(hl)
|
||||
inc hl
|
||||
ld (_PSGMusicSubstringRetAddr),hl ; save return address
|
||||
ld hl,(_PSGMusicStart)
|
||||
add hl,bc ; make substring current
|
||||
jp _intLoop
|
||||
|
||||
_high_part_Tone:
|
||||
; we got the last latch in A and the PSG data in B
|
||||
; and we have to check if the value should pass to PSG or not
|
||||
; PSG data can only be for channel 2, here
|
||||
ld a,b ; move PSG data in A
|
||||
ld (_PSGChan2HighTone),a ; save channel 2 tone HIGH data
|
||||
ld a,(_PSGChannel2SFX) ; channel 2 free?
|
||||
or a
|
||||
jr z,_send2PSG_B
|
||||
jp _intLoop
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void PSGSFXFrame (void) {
|
||||
/* ********************************************************************
|
||||
processes a SFX frame
|
||||
*/
|
||||
__asm
|
||||
ld a,(_PSGSFXStatus) ; check if we have got to play SFX
|
||||
or a
|
||||
ret z
|
||||
|
||||
ld a,(_PSGSFXSkipFrames) ; check if we have got to skip frames
|
||||
or a
|
||||
jp nz,_skipSFXFrame
|
||||
|
||||
ld hl,(_PSGSFXPointer) ; read current SFX address
|
||||
|
||||
_intSFXLoop:
|
||||
ld b,(hl) ; load a byte in B, temporary
|
||||
inc hl ; point to next byte
|
||||
ld a,(_PSGSFXSubstringLen) ; read substring len
|
||||
or a ; check if it is 0 (we are not in a substring)
|
||||
jr z,_SFXcontinue
|
||||
dec a ; decrease len
|
||||
ld (_PSGSFXSubstringLen),a ; save len
|
||||
jr nz,_SFXcontinue
|
||||
ld hl,(_PSGSFXSubstringRetAddr) ; substring over, retrieve return address
|
||||
|
||||
_SFXcontinue:
|
||||
ld a,b ; restore byte
|
||||
cp PSGData
|
||||
jp c,_SFXcommand ; if less than $40 then it is a command
|
||||
bit 4,a ; check if it is a volume byte
|
||||
jr z,_SFXoutbyte ; if not, output it
|
||||
bit 5,a ; check if it is volume for channel 2 or channel 3
|
||||
jr nz,_SFXvolumechn3
|
||||
ld (_PSGSFXChan2Volume),a
|
||||
jr _SFXoutbyte
|
||||
|
||||
_SFXvolumechn3:
|
||||
ld (_PSGSFXChan3Volume),a
|
||||
|
||||
_SFXoutbyte:
|
||||
out (PSGDataPort),a ; output the byte
|
||||
jp _intSFXLoop
|
||||
|
||||
_skipSFXFrame:
|
||||
dec a
|
||||
ld (_PSGSFXSkipFrames),a
|
||||
ret
|
||||
|
||||
_SFXcommand:
|
||||
cp PSGWait
|
||||
jr z,_SFXdone ; no additional frames
|
||||
jr c,_SFXotherCommands ; other commands?
|
||||
and #0x07 ; take only the last 3 bits for skip frames
|
||||
ld (_PSGSFXSkipFrames),a ; we got additional frames to skip
|
||||
_SFXdone:
|
||||
ld (_PSGSFXPointer),hl ; save current address
|
||||
ret ; frame done
|
||||
|
||||
_SFXotherCommands:
|
||||
cp PSGSubString
|
||||
jr nc,_SFXsubstring
|
||||
cp PSGEnd
|
||||
jr z,_sfxLoop
|
||||
cp PSGLoop
|
||||
jr z,_SFXsetLoopPoint
|
||||
|
||||
; ***************************************************************************
|
||||
; we should never get here!
|
||||
; if we do, it means the PSG SFX file is probably corrupted, so we just RET
|
||||
; ***************************************************************************
|
||||
|
||||
ret
|
||||
|
||||
_SFXsetLoopPoint:
|
||||
ld (_PSGSFXLoopPoint),hl
|
||||
jp _intSFXLoop
|
||||
|
||||
_sfxLoop:
|
||||
ld a,(_PSGSFXLoopFlag) ; is it a looping SFX?
|
||||
or a
|
||||
jp z,_PSGSFXStop ; No:stop it! (tail call optimization)
|
||||
ld hl,(_PSGSFXLoopPoint)
|
||||
ld (_PSGSFXPointer),hl
|
||||
jp _intSFXLoop
|
||||
|
||||
_SFXsubstring:
|
||||
sub PSGSubString-4 ; len is value - $08 + 4
|
||||
ld (_PSGSFXSubstringLen),a ; save len
|
||||
ld c,(hl) ; load substring address (offset)
|
||||
inc hl
|
||||
ld b,(hl)
|
||||
inc hl
|
||||
ld (_PSGSFXSubstringRetAddr),hl ; save return address
|
||||
ld hl,(_PSGSFXStart)
|
||||
add hl,bc ; make substring current
|
||||
jp _intSFXLoop
|
||||
__endasm;
|
||||
}
|
31
presets/sms-sms-libcv/PSGlib.h
Normal file
31
presets/sms-sms-libcv/PSGlib.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* **************************************************
|
||||
PSGlib - C programming library for the SEGA PSG
|
||||
( part of devkitSMS - github.com/sverx/devkitSMS )
|
||||
************************************************** */
|
||||
|
||||
#define PSG_STOPPED 0
|
||||
#define PSG_PLAYING 1
|
||||
|
||||
#define SFX_CHANNEL2 #0x01
|
||||
#define SFX_CHANNEL3 #0x02
|
||||
#define SFX_CHANNELS2AND3 SFX_CHANNEL2|SFX_CHANNEL3
|
||||
|
||||
void PSGPlay (void *song);
|
||||
void PSGCancelLoop (void);
|
||||
void PSGPlayNoRepeat (void *song);
|
||||
void PSGStop (void);
|
||||
void PSGResume (void);
|
||||
unsigned char PSGGetStatus (void);
|
||||
void PSGSetMusicVolumeAttenuation (unsigned char attenuation);
|
||||
|
||||
void PSGSFXPlay (void *sfx, unsigned char channels);
|
||||
void PSGSFXPlayLoop (void *sfx, unsigned char channels);
|
||||
void PSGSFXCancelLoop (void);
|
||||
void PSGSFXStop (void);
|
||||
unsigned char PSGSFXGetStatus (void);
|
||||
|
||||
void PSGSilenceChannels (void);
|
||||
void PSGRestoreVolumes (void);
|
||||
|
||||
void PSGFrame (void);
|
||||
void PSGSFXFrame (void);
|
|
@ -1,3 +1,4 @@
|
|||
/*{w:8,h:8,bpp:1,count:256,brev:1,np:4,pofs:1,sl:4}*/
|
||||
const unsigned char CHR_GENERIC[8192] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,16 +22,16 @@ byte reverse_bits(byte n) {
|
|||
void flip_sprite_patterns(word dest, const byte* patterns, word len) {
|
||||
word i;
|
||||
for (i=0; i<len; i++) {
|
||||
cvu_voutb(reverse_bits(*patterns++), dest++ ^ 16); // swap left/right chars
|
||||
cvu_voutb(reverse_bits(*patterns++), dest++); // swap left/right chars
|
||||
}
|
||||
}
|
||||
|
||||
void clrscr() {
|
||||
cvu_vmemset(IMAGE, 0, COLS*ROWS);
|
||||
cvu_vmemset(IMAGE, 0, COLS*2*ROWS);
|
||||
}
|
||||
|
||||
word getimageaddr(byte x, byte y) {
|
||||
return IMAGE + y*COLS + x;
|
||||
return IMAGE + y*COLS*2 + x;
|
||||
}
|
||||
|
||||
byte getchar(byte x, byte y) {
|
||||
|
@ -103,11 +103,11 @@ __endasm;
|
|||
|
||||
void vdp_setup() {
|
||||
cv_set_screen_active(false);
|
||||
cv_set_screen_mode(CV_SCREENMODE_STANDARD);
|
||||
cv_set_image_table(IMAGE);
|
||||
cv_set_character_pattern_t(PATTERN);
|
||||
cv_set_color_table(COLOR);
|
||||
cv_set_sprite_pattern_table(SPRITE_PATTERNS);
|
||||
cv_set_screen_mode(CV_SCREENMODE_4_224);
|
||||
cv_set_character_pattern_t(PATTERN | 0x3000);
|
||||
cv_set_image_table(IMAGE | 0x400);
|
||||
// cv_set_color_table(COLOR | 0xfff);
|
||||
// cv_set_sprite_pattern_table(SPRITE_PATTERNS | 0x1800);
|
||||
cv_set_sprite_attribute_table(SPRITES);
|
||||
cv_set_sprite_big(true);
|
||||
}
|
||||
|
|
|
@ -3,21 +3,17 @@
|
|||
#define _CV_COMMON_H
|
||||
|
||||
/* VRAM map
|
||||
0x0000 - 0x17ff character pattern table
|
||||
0x1800 - 0x1aff image table
|
||||
0x2000 - 0x37ff color table
|
||||
0x3800 - 0x3bff sprite pattern table
|
||||
0x0000 - 0x3fff character pattern table
|
||||
0x3000 - 0x36ff image table
|
||||
0x3c00 - 0x3fff sprite attribute table
|
||||
*/
|
||||
|
||||
#define PATTERN ((const cv_vmemp)0x0000)
|
||||
#define IMAGE ((const cv_vmemp)0x1800)
|
||||
#define COLOR ((const cv_vmemp)0x2000)
|
||||
#define SPRITE_PATTERNS ((const cv_vmemp)0x3800)
|
||||
#define IMAGE ((const cv_vmemp)0x3000)
|
||||
#define SPRITES ((const cv_vmemp)0x3c00)
|
||||
|
||||
#define COLS 32
|
||||
#define ROWS 24
|
||||
#define ROWS 28
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef signed char sbyte;
|
||||
|
|
14
tools/bin2arr.py
Normal file
14
tools/bin2arr.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
import sys
|
||||
|
||||
out = sys.stdout
|
||||
chr = open(sys.argv[1],'rb').read()
|
||||
|
||||
out.write('const unsigned char ARRAY[%d] = {\n' % len(chr))
|
||||
|
||||
for i in range(0,len(chr)):
|
||||
out.write('0x%02x, ' % ord(chr[i]))
|
||||
if (i & 7) == 7:
|
||||
out.write('\n')
|
||||
|
||||
out.write('\n};\n')
|
Loading…
Reference in New Issue
Block a user