sms: redid climber; added PSGLib

This commit is contained in:
Steven Hugg 2019-08-07 09:26:53 -04:00
parent 138ee806cb
commit 5b39cd51ed
8 changed files with 1181 additions and 272 deletions

View File

@ -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

View 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;
}

View 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);

View File

@ -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

View File

@ -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);
}

View File

@ -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
View 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')