From dba86085618817a4d07d2aa5cc8e0be11ac5184a Mon Sep 17 00:00:00 2001 From: CrashOveride95 Date: Fri, 18 Dec 2020 22:36:09 -0500 Subject: [PATCH] *it still lags* --- Makefile | 2 +- sm64.ld | 5 + src/audio/data.c | 2 +- src/buffers/adpcmbuf.c | 23 ++ src/buffers/buffers.c | 13 +- src/buffers/framebuffers.c | 2 +- src/buffers/hvqbuf.c | 24 ++ src/buffers/hvqmwork.c | 43 ++++ src/game/game_init.c | 4 +- src/hvqm/cfbkeep.c | 110 +++++++++ src/hvqm/getrecord.c | 77 +++++++ src/{game => hvqm}/hvqm.c | 72 +----- src/{game => hvqm}/hvqm.h | 52 +++-- src/hvqm/system.c | 46 ++++ src/{game => hvqm}/timekeeper.c | 385 ++++++++++++++++++++------------ 15 files changed, 617 insertions(+), 243 deletions(-) create mode 100644 src/buffers/adpcmbuf.c create mode 100644 src/buffers/hvqbuf.c create mode 100644 src/buffers/hvqmwork.c create mode 100644 src/hvqm/cfbkeep.c create mode 100644 src/hvqm/getrecord.c rename src/{game => hvqm}/hvqm.c (82%) rename src/{game => hvqm}/hvqm.h (81%) create mode 100644 src/hvqm/system.c rename src/{game => hvqm}/timekeeper.c (53%) diff --git a/Makefile b/Makefile index c4611696..1633dbc9 100644 --- a/Makefile +++ b/Makefile @@ -230,7 +230,7 @@ ACTOR_DIR := actors LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h))) # Directories containing source files -SRC_DIRS := src src/usb src/engine src/game src/audio src/menu src/buffers actors levels bin data assets asm lib sound +SRC_DIRS := src src/usb src/engine src/game src/hvqm src/audio src/menu src/buffers actors levels bin data assets asm lib sound BIN_DIRS := bin bin/$(VERSION) # File dependencies and variables for specific files diff --git a/sm64.ld b/sm64.ld index c77f9510..b9dfcde7 100755 --- a/sm64.ld +++ b/sm64.ld @@ -85,6 +85,9 @@ SECTIONS BEGIN_NOLOAD(buffers) { BUILD_DIR/src/buffers/buffers.o(.bss*); + BUILD_DIR/src/buffers/hvqmwork.o(.bss*); + BUILD_DIR/src/buffers/adpcmbuf.o(.bss*); + BUILD_DIR/src/buffers/hvqbuf.o(.bss*); BUILD_DIR/src/audio/globals_start.o(.bss*); BUILD_DIR/src/audio/synthesis.o(.bss*); BUILD_DIR/src/audio/heap.o(.bss*); @@ -116,6 +119,7 @@ SECTIONS BUILD_DIR/asm/entry.o(.text); BUILD_DIR/src/game*.o(.text); + BUILD_DIR/src/hvqm*.o(.text); #ifdef UNF BUILD_DIR/src/usb*.o(.text); #endif @@ -164,6 +168,7 @@ SECTIONS { BUILD_DIR/src/game*.o(.bss*); BUILD_DIR/src/game*.o(.sbss*); + BUILD_DIR/src/hvqm*.o(.bss*); BUILD_DIR/src/usb*.o(.bss*); BUILD_DIR/src/usb*.o(.sbss*); BUILD_DIR/src/audio*.o(.bss*); diff --git a/src/audio/data.c b/src/audio/data.c index 3817c7ab..4fdc13e5 100644 --- a/src/audio/data.c +++ b/src/audio/data.c @@ -927,7 +927,7 @@ f32 D_EU_802298D0; s32 gRefreshRate; #endif -s16 *gAiBuffers[NUMAIBUFFERS]; +ALIGNED8 s16 *gAiBuffers[NUMAIBUFFERS]; s16 gAiBufferLengths[NUMAIBUFFERS]; #if defined(VERSION_JP) || defined(VERSION_US) diff --git a/src/buffers/adpcmbuf.c b/src/buffers/adpcmbuf.c new file mode 100644 index 00000000..fda80c08 --- /dev/null +++ b/src/buffers/adpcmbuf.c @@ -0,0 +1,23 @@ +/* + * N64-HVQM2 library Sample program + * + * FILE : adpcmbuf.c + * + * Copyright (C) 1998,1999 NINTENDO Co.,Ltd. + * + */ + +/* 1999-02-22 */ + +#include +#include +#include +#include + +/* + * Buffer for audio records (ADPCM data) read from the HVQM2 data. + * (Note) Please locate at a 16byte aligned address with the spec file. + */ +ALIGNED16 u8 adpcmbuf[AUDIO_RECORD_SIZE_MAX]; + +/* end */ diff --git a/src/buffers/buffers.c b/src/buffers/buffers.c index da5cf2f8..71a42047 100644 --- a/src/buffers/buffers.c +++ b/src/buffers/buffers.c @@ -2,7 +2,7 @@ #include #include "buffers.h" -#include +#include #include "config.h" ALIGNED8 u8 gDecompressionHeap[0xD000]; @@ -42,14 +42,3 @@ ALIGNED8 u8 gAudioSPTaskYieldBuffer[OS_YIELD_AUDIO_SIZE]; #if !defined(F3DEX_GBI_SHARED) && !defined(VERSION_EU) ALIGNED8 u8 gUnusedThread2Stack[0x1400]; #endif - -ALIGNED16 u8 hvqbuf[HVQ_DATASIZE_MAX]; - -ALIGNED16 u64 hvq_yieldbuf[HVQM2_YIELD_DATA_SIZE/8]; -ALIGNED16 u8 adpcmbuf[AUDIO_RECORD_SIZE_MAX]; -ALIGNED16 u16 hvqwork[(MAXWIDTH/8)*(MAXHEIGHT/4)*4]; - -// Data area for the HVQ microcode -ALIGNED16 HVQM2Info hvq_spfifo[HVQ_SPFIFO_SIZE]; - - diff --git a/src/buffers/framebuffers.c b/src/buffers/framebuffers.c index ab5e62f7..7f4a07e5 100644 --- a/src/buffers/framebuffers.c +++ b/src/buffers/framebuffers.c @@ -3,4 +3,4 @@ #include "config.h" // 0x70800 bytes -u16 gFrameBuffers[3][SCREEN_WIDTH * SCREEN_HEIGHT]; +__attribute__((aligned (64))) u16 gFrameBuffers[3][SCREEN_WIDTH * SCREEN_HEIGHT]; diff --git a/src/buffers/hvqbuf.c b/src/buffers/hvqbuf.c new file mode 100644 index 00000000..351fb040 --- /dev/null +++ b/src/buffers/hvqbuf.c @@ -0,0 +1,24 @@ +/* + * N64-HVQM2 library Sample program + * + * FILE : hvqbuf.c + * + * Copyright (C) 1998,1999 NINTENDO Co.,Ltd. + * + */ + +/* 1999-02-22 */ + +#include +#include +#include +#include + +/* + * Buffer for video records (HVQM2 compressed data) read from + * the HVQM2 data. + * (Note) Please locate at a 16byte aligned address with the spec file. + */ +ALIGNED16 u8 hvqbuf[HVQ_DATASIZE_MAX]; + +/* end */ diff --git a/src/buffers/hvqmwork.c b/src/buffers/hvqmwork.c new file mode 100644 index 00000000..c214983c --- /dev/null +++ b/src/buffers/hvqmwork.c @@ -0,0 +1,43 @@ +/* + * N64-HVQM2 library Sample program + * + * FILE : hvqmwork.c + * + * Copyright (C) 1998,1999 NINTENDO Co.,Ltd. + * + */ + +/* 1999-02-12 */ + +#include +#include +#include + +/* + * Work area needed by the HVQM2 library + * + * (Note) MAXWIDTH and MAXHEIGHT are set with Makefile + */ +#if 0 +/* + * If the compressed data for decoding is only in 4:1:1 format: + */ + u16 hvqwork[(MAXWIDTH/8)*(MAXHEIGHT/8)*6]; +#else +/* + * If 4:2:2 data will (also) be decoded: + */ + u16 hvqwork[(MAXWIDTH/8)*(MAXHEIGHT/4)*4]; +#endif + +/* + * Data area for the HVQ microcode + */ +HVQM2Info hvq_spfifo[HVQ_SPFIFO_SIZE]; + +/* + * Buffer for RSP task yield + */ +u64 hvq_yieldbuf[HVQM2_YIELD_DATA_SIZE/8]; + +/* end */ diff --git a/src/game/game_init.c b/src/game/game_init.c index 8e6d3fc2..5702fd60 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -19,7 +19,7 @@ #include "segment2.h" #include "segment_symbols.h" #include "rumble_init.h" -#include "hvqm.h" +#include #include "usb/usb.h" #include "usb/debug.h" #include @@ -354,7 +354,7 @@ void display_and_vsync(void) { // this function records distinct inputs over a 255-frame interval to RAM locations and was likely // used to record the demo sequences seen in the final game. This function is unused. -static void record_demo(void) { +UNUSED static void record_demo(void) { // record the player's button mask and current rawStickX and rawStickY. u8 buttonMask = ((gPlayer1Controller->buttonDown & (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON)) >> 8) diff --git a/src/hvqm/cfbkeep.c b/src/hvqm/cfbkeep.c new file mode 100644 index 00000000..1b076386 --- /dev/null +++ b/src/hvqm/cfbkeep.c @@ -0,0 +1,110 @@ +/* + * N64-HVQM2 library Sample program + * + * FILE : cfbkeep.c (frame buffer management) + * + * Copyright (C) 1998,1999 NINTENDO Co.,Ltd. + * + */ + +/* 1998-12-16 */ + +#include +#include +#include "hvqm.h" +#include "buffers/framebuffers.h" + +/*********************************************************************** + * Array maintaining the state of the frame buffer + * (cfb_status[N] corresponds to the frame buffer itself cfb[N] + ***********************************************************************/ +u32 cfb_status[NUM_CFBs]; + +/*********************************************************************** + * + * void init_cfb(void) + * + * Explanation + * Clears all frame buffers and initializes state of frame buffer + * + ***********************************************************************/ +void init_cfb(void) { + int i, j; + + for ( i = 0; i < NUM_CFBs; i++ ) { + for ( j = 0; j < SCREEN_WD*SCREEN_HT; j++ ) gFrameBuffers[i][j] = 0; + cfb_status[i] = CFB_FREE; + } +} + +/*********************************************************************** + * + * void keep_cfb(int cfbno) + * + * Explanation + * Frame buffer indicated by the index cfbno is protected by + * being made unavailable. + * + ***********************************************************************/ +void +keep_cfb( int cfbno ) +{ + cfb_status[cfbno] |= CFB_PRECIOUS; +} + +/*********************************************************************** + * + * void release_cfb(int cfbno) + * + * Explanation + * Frame buffer indicated by the index cfbno is released + * from protection. + * + ***********************************************************************/ +void +release_cfb( int bufno ) +{ + if ( bufno >= 0 ) cfb_status[bufno] &= ~CFB_PRECIOUS; +} + +/*********************************************************************** + * + * void release_all_cfb(void) + * + * Explanation + * All frame buffers are released from protection. + * + ***********************************************************************/ +void +release_all_cfb(void) +{ + int i; + for ( i = 0; i < NUM_CFBs; i++ ) cfb_status[i] &= ~CFB_PRECIOUS; +} + +/*********************************************************************** + * + * int get_cfb() + * + * Explanation + * Search for available frame buffer and return that index. + * Thread yields until an available frame buffer is found. + * + * Returned value + * The index of the available frame buffer. + * + ***********************************************************************/ +int +get_cfb() +{ + int cfbno; + + for ( ; ; ) { + for ( cfbno = 0; cfbno < NUM_CFBs; cfbno++ ) + if ( cfb_status[cfbno] == 0 ) + return cfbno; + osYieldThread(); + } +} + +/* end */ diff --git a/src/hvqm/getrecord.c b/src/hvqm/getrecord.c new file mode 100644 index 00000000..61ad39e8 --- /dev/null +++ b/src/hvqm/getrecord.c @@ -0,0 +1,77 @@ +/* + * N64-HVQM2 library Sample program + * + * FILE : getrecord.c (module for reading HVQM2 records) + * + * Copyright (C) 1998,1999 NINTENDO Co.,Ltd. + * + */ + +/* 1999-02-22 */ + +#include +#include +#include "hvqm.h" + +/* + * u8 *get_record(HVQM2Record *headerbuf, void *bodybuf, + * u16 type, u8 *stream, OSIoMesg *mb, OSMesgQueue *mq) + * + * Arguments + * headerbuf Buffer storing the record header (HVQM2Record structure) + * bodybuf Buffer storing the record's data (record body) + * type The requested record type + * stream The current HVQM2 record address + * mb I/O message block request sent to PI manager + * mq Message queue receiving notice of DMA end from PI manager + * + * Explanation: + * Finds the next record of the specified "type" from the HVQM2 data + * address "stream" in ROM, and reads the record header into "headerbuf" + * and the data body into "bodybuf." Specify HVQM2_AUDIO for "type" to + * read an audio record, and specify HVQM2_VIDEO to read a video record. + * + * This function assumes that the reading of audio records and video + * records is performed in parallel by separate threads. Accordingly, + * you must prepare in advance a separate mb (I/O message block request + * sent to PI manager) and separate mq (message queue receiving notice + * of DMA end from PI manager) for audio records and video records. + * + * The reading of audio records has priority over the reading of + * video records. + * + * "stream" must have 2byte alignment. Please give headerbuf and + * bodybuf 16byte alignment to conform with the R4300 data cache line + * size. + * + * Please be sure to reserve sufficient space for bodybuf. + * + * + * Returned value: + * The address of the next record + */ +u8 * +get_record( HVQM2Record *headerbuf, void *bodybuf, u16 type, u8 *stream, OSIoMesg *mb, OSMesgQueue *mq ) +{ + u16 record_type; + u32 record_size; + s32 pri; + + pri = (type == HVQM2_AUDIO) ? OS_MESG_PRI_HIGH : OS_MESG_PRI_NORMAL; + for ( ; ; ) { + romcpy( headerbuf, stream, sizeof(HVQM2Record), pri, mb, mq ); + stream += sizeof(HVQM2Record); + record_type = load16( headerbuf->type ); + record_size = load32( headerbuf->size ); + if ( record_type == type ) break; + stream += record_size; + } + + if ( record_size > 0 ) { + romcpy( bodybuf, stream, record_size, pri, mb, mq ); + stream += record_size; + } + return stream; +} + +/* end */ diff --git a/src/game/hvqm.c b/src/hvqm/hvqm.c similarity index 82% rename from src/game/hvqm.c rename to src/hvqm/hvqm.c index 96c6075a..9aabc1b6 100644 --- a/src/game/hvqm.c +++ b/src/hvqm/hvqm.c @@ -6,6 +6,7 @@ #include #include "hvqm.h" +#define AUDIO_DMA_MSG_SIZE 1 static OSIoMesg audioDmaMesgBlock; static OSMesgQueue audioDmaMessageQ; static OSMesg audioDmaMessages[AUDIO_DMA_MSG_SIZE]; @@ -57,77 +58,6 @@ static ADPCMstate adpcm_state; /* Buffer for state information passed to the ADP extern u8 _capcomSegmentRomStart[]; extern u8 _seinSegmentRomStart[]; -u32 cfb_status[NUM_CFBs]; - -// Clears all frame buffers and initializes state of frame buffer -void init_cfb(void) { - int i, j; - - for ( i = 0; i < NUM_CFBs; i++ ) { - for ( j = 0; j < SCREEN_WD*SCREEN_HT; j++ ) gFrameBuffers[i][j] = 0; - cfb_status[i] = CFB_FREE; - } -} - -// Frame buffer indicated by the index cfbno is protected by being made unavailable. -void keep_cfb( int cfbno ) { - cfb_status[cfbno] |= CFB_PRECIOUS; -} - -//Frame buffer indicated by the index cfbno is released from protection. -void release_cfb( int bufno ) { - if ( bufno >= 0 ) cfb_status[bufno] &= ~CFB_PRECIOUS; -} - -// All frame buffers are released from protection. -void release_all_cfb(void) { - int i; - for ( i = 0; i < NUM_CFBs; i++ ) cfb_status[i] &= ~CFB_PRECIOUS; -} - -// Search for available frame buffer and return that index. -// Thread yields until an available frame buffer is found. -int get_cfb() { - int cfbno; - - for ( ; ; ) { - for ( cfbno = 0; cfbno < NUM_CFBs; cfbno++ ) - if ( cfb_status[cfbno] == CFB_FREE ) - return cfbno; - osYieldThread(); - } -} - -void romcpy(void *dest, void *src, u32 len, s32 pri, OSIoMesg *mb, OSMesgQueue *mq) { - osInvalDCache(dest, (s32)len); - while (osPiStartDma(mb, pri, OS_READ, (u32)src, dest, len, mq) == -1) {} - osRecvMesg(mq, (OSMesg *)NULL, OS_MESG_BLOCK); -} - -u8 *get_record(HVQM2Record *headerbuf, void *bodybuf, u16 type, u8 *stream, OSIoMesg *mb, OSMesgQueue *mq) { - u16 record_type; - u32 record_size; - s32 pri, i; - - pri = (type == HVQM2_AUDIO) ? OS_MESG_PRI_HIGH : OS_MESG_PRI_NORMAL; - - while (1) { - romcpy(headerbuf, stream, sizeof(HVQM2Record), pri, mb, mq); - stream += sizeof(HVQM2Record); - record_type = load16(headerbuf->type); - record_size = load32(headerbuf->size); - if (record_type == type) break; - stream += record_size; - } - - if (record_size > 0) { - romcpy(bodybuf, stream, record_size, pri, mb, mq); - stream += record_size; - } - - return stream; -} - static u32 next_audio_record( void *pcmbuf ) { ALIGNED16 u8 header_buffer[sizeof(HVQM2Record)+16]; HVQM2Record *record_header; diff --git a/src/game/hvqm.h b/src/hvqm/hvqm.h similarity index 81% rename from src/game/hvqm.h rename to src/hvqm/hvqm.h index 40a734d6..ba994c8c 100644 --- a/src/game/hvqm.h +++ b/src/hvqm/hvqm.h @@ -30,26 +30,11 @@ #define APP_GFX_UCODE_HVQ 6 /* HVQ2 microcode */ -#define AUDIO_DMA_MSG_SIZE 1 - - #define CFB_FREE 0 /* Available */ #define CFB_PRECIOUS (1<<0) /* Constrained for decoding of next frame */ #define CFB_SHOWING (1<<1) /* Waiting to display or displaying */ -typedef u32 (*tkAudioProc)(void *pcmbuf); -typedef tkAudioProc (*tkRewindProc)(void); - -void createTimekeeper(); -void tkStart(tkRewindProc rewind, u32 samples_per_sec); -void tkPushVideoframe(void *vaddr, u32 *statP, u64 disptime); -u64 tkGetTime(void); -void tkStop(void); -void createHvqmThread(void); - - - #define NUM_CFBs 3 #define STACKSIZE 0x2000 #define PCM_CHANNELS 2 /* Number of channels */ @@ -72,6 +57,12 @@ void createHvqmThread(void); #define PCMBUF_ALIGNED ((PCMBUF_SPREAD+(PCM_ALIGN-1))&(~(PCM_ALIGN-1))) /* pcmbuf[i] address is aligned */ #define PCMBUF_SIZE 0xA00 +/* + * Macro for loading multi-byte data from buffer holding data from stream + */ +#define load32(from) (*(u32*)&(from)) +#define load16(from) (*(u16*)&(from)) + /* * Thread ID and priority */ @@ -92,5 +83,36 @@ void createHvqmThread(void); typedef u32 (*tkAudioProc)(void *pcmbuf); typedef tkAudioProc (*tkRewindProc)(void); +void createTimekeeper(); +void tkStart(tkRewindProc rewind, u32 samples_per_sec); +void tkPushVideoframe(void *vaddr, u32 *statP, u64 disptime); +u64 tkGetTime(void); +void tkStop(void); +void createHvqmThread(void); + +/* + * in system.c + */ +void romcpy(void *dest, void *src, u32 len, s32 pri, OSIoMesg *mb, OSMesgQueue *mq); + +/* + * in getrecord.c + */ +u8 *get_record(HVQM2Record *headerbuf, void *bodybuf, u16 type, u8 *stream, OSIoMesg *mb, OSMesgQueue *mq); + +/* + * in cfbkeep.c + */ +extern u32 cfb_status[NUM_CFBs]; + +void init_cfb(void); +void keep_cfb(int cfbno); +void release_cfb(int cfbno); +void release_all_cfb(void); +int get_cfb(); + +typedef u32 (*tkAudioProc)(void *pcmbuf); +typedef tkAudioProc (*tkRewindProc)(void); + #endif // HVQM_H \ No newline at end of file diff --git a/src/hvqm/system.c b/src/hvqm/system.c new file mode 100644 index 00000000..175dcdd0 --- /dev/null +++ b/src/hvqm/system.c @@ -0,0 +1,46 @@ +/* + * N64-HVQM2 library Sample program + * + * FILE : system.c (boot program/system utility) + * + * Copyright (C) 1998,1999 NINTENDO Co.,Ltd. + * + */ + +/* 1998-12-15 */ + +#include +#include +#include "hvqm.h" + +/*********************************************************************** + * + * void romcpy(void *dest, void *src, u32 len, s32 pri, OSIoMesg *mb, + * OSMesgQueue *mq) + * + * Arguments + * dest DRAM address + * src PI device (ROM) address + * len Transfer length (bytes) + * pri Priority of the transfer request + * mb I/O message block request + * mq Message queue receiving notification of end of DMA + * + * Explanation + * DMA transfers "len" bytes from ROM address "SRC" to DRAM + * address "dest" and returns after waiting for end of DMA. The + * data cache of the transfer destination in DRAM is invalidated + * ahead of time. + * + * The parameters have the same meaning as for osPiStartDma() + * + ***********************************************************************/ +void +romcpy(void *dest, void *src, u32 len, s32 pri, OSIoMesg *mb, OSMesgQueue *mq) +{ + osInvalDCache( dest, (s32)len ); + while ( osPiStartDma( mb, pri, OS_READ, (u32)src, dest, len, mq ) == -1 ) {} + osRecvMesg( mq, (OSMesg *)NULL, OS_MESG_BLOCK ); +} + +/* end */ diff --git a/src/game/timekeeper.c b/src/hvqm/timekeeper.c similarity index 53% rename from src/game/timekeeper.c rename to src/hvqm/timekeeper.c index 31b00524..5cd55f7b 100644 --- a/src/game/timekeeper.c +++ b/src/hvqm/timekeeper.c @@ -1,42 +1,9 @@ #include +#include #include "hvqm.h" #include "audio/data.h" #include "buffers/framebuffers.h" -#define NUM_CFBs 3 -#define STACKSIZE 0x2000 -#define PCM_CHANNELS 2 /* Number of channels */ -#define PCM_CHANNELS_SHIFT 1 /* log2(PCM_CHANNELS) */ -#define PCM_ALIGN 2 /* Alignment of number of samples to send */ -#define PCM_BYTES_PER_SAMPLE (2 * PCM_CHANNELS) /* Number of bytes in one sample */ -#define PCM_BYTES_PER_SAMPLE_SHIFT 2 /* log2(PCM_BYTES_PER_SAMPLE) */ - -/* - * Audio record definitions - */ -#define AUDIO_SAMPLE_BITS 4 -#define AUDIO_SAMPLES_MAX (((AUDIO_RECORD_SIZE_MAX-sizeof(HVQM2Audio))*8/AUDIO_SAMPLE_BITS)+1) /* Maximum number of records per sample */ - -/* - * Thread ID and priority - */ -#define IDLE_THREAD_ID 1 -#define MAIN_THREAD_ID 3 -#define HVQM_THREAD_ID 7 -#define TIMEKEEPER_THREAD_ID 8 -#define DA_COUNTER_THREAD_ID 9 - -#define IDLE_PRIORITY 10 -#define MAIN_PRIORITY 10 -#define HVQM_PRIORITY 11 -#define TIMEKEEPER_PRIORITY 12 -#define DA_COUNTER_PRIORITY 13 - -#define PI_COMMAND_QUEUE_SIZE 4 - -typedef u32 (*tkAudioProc)(void *pcmbuf); -typedef tkAudioProc (*tkRewindProc)(void); - /*********************************************************************** * Timekeeper thread ***********************************************************************/ @@ -158,14 +125,22 @@ static u64 samples_played; ***********************************************************************/ static OSTime last_time; -#define OS_CLOCK_RATE 62500000LL -#define OS_CPU_COUNTER (OS_CLOCK_RATE*3/4) -#define OS_NSEC_TO_CYCLES(n) (((u64)(n)*(OS_CPU_COUNTER/15625000LL))/(1000000000LL/15625000LL)) -#define OS_USEC_TO_CYCLES(n) (((u64)(n)*(OS_CPU_COUNTER/15625LL))/(1000000LL/15625LL)) -#define OS_CYCLES_TO_NSEC(c) (((u64)(c)*(1000000000LL/15625000LL))/(OS_CPU_COUNTER/15625000LL)) -#define OS_CYCLES_TO_USEC(c) (((u64)(c)*(1000000LL/15625LL))/(OS_CPU_COUNTER/15625LL)) - -u64 tkGetTime(void) { +/*********************************************************************** + * + * u64 tkGetTime(void) + * + * Explanation + * The time that has passed since the start of HVQM2 movie playback + * is calculated back from the number of samples that have finished + * playing (samples_played) and the sampling rate (samples_per_sec). + * The time after samples_played was first updated is supplemented + * with osGetTime(). + * + * Returned value + * The time that has passed since playback of the HVQM2 movie started + * + ***********************************************************************/ +u64 tkGetTime(void){ u64 retval; if (!clock_alive) return 0; @@ -175,28 +150,62 @@ u64 tkGetTime(void) { return retval; } -static void tkClockDisable(void) { - clock_alive = 0; +/*********************************************************************** + * + * void tkClockDisable(void) + * + * Explanation + * Disable measurement of time from start of HVQM2 movie playback + * + ***********************************************************************/ +static void tkClockDisable(void) +{ + clock_alive = 0; } -static void tkClockStart(void) { - samples_played = 0; +/*********************************************************************** + * + * void tkClockStart(void) + * + * Explanation + * Start measurement of time from start of HVQM2 movie playback + * + ***********************************************************************/ +static void tkClockStart(void) +{ + samples_played = 0; + last_time = osGetTime(); + clock_alive = 1; +} + +/*********************************************************************** + * + * daCounterProc - Audio DA counter thread procedure + * + * Counts the number of audio samples that have finished playing. + * + ***********************************************************************/ +static void daCounterProc(void UNUSED*argument) +{ + for ( ; ; ) { + osRecvMesg( &aiMessageQ, NULL, OS_MESG_BLOCK ); last_time = osGetTime(); - clock_alive = 1; + if ( aiDAsamples > 0 ) --pcmBufferCount; + samples_played += aiDAsamples; + aiDAsamples = aiFIFOsamples; + aiFIFOsamples = 0; + } } -static void daCounterProc(void *argument) { - while (1) { - osRecvMesg( &aiMessageQ, NULL, OS_MESG_BLOCK ); - last_time = osGetTime(); - if (aiDAsamples > 0) --pcmBufferCount; - samples_played += aiDAsamples; - aiDAsamples = aiFIFOsamples; - aiFIFOsamples = 0; - } -} - -static void timekeeperProc(void *argument) { +/*********************************************************************** + * + * timekeeperProc - Timekeeper thread procedure + * + * Plays the HVQM2 movie's audio records and takes charge of + * completed frame buffers, displaying them at the scheduled times. + * + ***********************************************************************/ +static void timekeeperProc(void UNUSED*argument) { TKCMD *cmd; void *showing_cfb = NULL; /* Frame buffer being displayed */ u32 *showing_cfb_statP = NULL; /* Pointer to state flag of the frame buffer being displayed */ @@ -211,6 +220,7 @@ static void timekeeperProc(void *argument) { tkAudioProc audioproc; OSTime present_vtime; + /* Acquire retrace event */ osCreateMesgQueue( &viMessageQ, viMessages, VI_MSG_SIZE ); osViSetEvent( &viMessageQ, 0, 1 ); @@ -228,9 +238,12 @@ static void timekeeperProc(void *argument) { osRecvMesg(&tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_BLOCK); - while (cmd == NULL) { - osSendMesg(&tkResMesgQ, NULL, OS_MESG_BLOCK); - osRecvMesg(&tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_BLOCK); + /* + * Wait for movie playback command + */ + while ( cmd == NULL ) { + osSendMesg( &tkResMesgQ, NULL, OS_MESG_BLOCK ); + osRecvMesg( &tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_BLOCK ); } tkClockDisable(); @@ -259,7 +272,7 @@ static void timekeeperProc(void *argument) { next_pcmbufno = 0; pcm_mod_samples = 0; - osSendMesg(&tkResMesgQ, NULL, OS_MESG_BLOCK); + osSendMesg( &tkResMesgQ, NULL, OS_MESG_BLOCK ); /* Notification that playback preparations are finished */ present_vtime = osGetTime(); @@ -267,7 +280,9 @@ static void timekeeperProc(void *argument) { !audio_done || audioRingCount > 0 || aiFIFOsamples > 0) { u64 present_time; OSTime last_vtime; - + /* + * Block until retrace comes + */ osRecvMesg(&viMessageQ, NULL, OS_MESG_BLOCK); last_vtime = present_vtime; @@ -297,89 +312,118 @@ static void timekeeperProc(void *argument) { pushed_cfb_statP = NULL; } - if ( video_started || audioRingCount > 0 || audio_done ) { - while ( videoRingCount ) { - void *next_cfb; - if ( videoRing[videoRingRead].disptime > present_time ) break; - if ( pushed_cfb_statP != NULL ) *pushed_cfb_statP &= ~CFB_SHOWING; - pushed_cfb = videoRing[videoRingRead].vaddr; - pushed_cfb_statP = videoRing[videoRingRead].statP; - *pushed_cfb_statP |= CFB_SHOWING; - osViSwapBuffer( pushed_cfb ); - if ( ++videoRingRead == VIDEO_RING_SIZE ) videoRingRead = 0; - --videoRingCount; - } + /* + * Push the next frame buffer to the VI + * + * Before the start of video display (video_started == 0), + * wait until audio data is prepared for playback + * (audioRingCount > 0). + */ + if ( video_started || audioRingCount > 0 || audio_done ) { + while ( videoRingCount ) { + if ( videoRing[videoRingRead].disptime > present_time ) break; + if ( pushed_cfb_statP != NULL ) *pushed_cfb_statP &= ~CFB_SHOWING; + pushed_cfb = videoRing[videoRingRead].vaddr; + pushed_cfb_statP = videoRing[videoRingRead].statP; + *pushed_cfb_statP |= CFB_SHOWING; + osViSwapBuffer( pushed_cfb ); + if ( ++videoRingRead == VIDEO_RING_SIZE ) videoRingRead = 0; + --videoRingCount; + } + } + + /* + * Push the next PCM data to the AI + * + * Before the start of video display (video_started == 0) + * wait until the display of the first image frame + * has begun (video_started == 1). + */ + if ( video_started ) { + osSetThreadPri( NULL, (OSPri)(DA_COUNTER_PRIORITY + 1) ); + while ( audioRingCount > 0 && aiFIFOsamples == 0 ) { + void *buffer = audioRing[audioRingRead].buf; + u32 length = audioRing[audioRingRead].len; + if ( osAiGetStatus() & AI_STATUS_FIFO_FULL ) break; + if ( osAiSetNextBuffer( buffer, length ) == -1 ) break; + aiFIFOsamples = length >> PCM_BYTES_PER_SAMPLE_SHIFT; + if ( ! audio_started ) { + /* + * Audio playback has begun. + * Here the time count starts to synchronize audio and video + */ + audio_started = 1; + tkClockStart(); } - - if ( video_started ) { - osSetThreadPri( NULL, (OSPri)(DA_COUNTER_PRIORITY + 1) ); - while ( audioRingCount > 0 && aiFIFOsamples == 0 ) { - void *buffer = audioRing[audioRingRead].buf; - u32 length = audioRing[audioRingRead].len; - //!if ( osAiGetStatus() & AI_STATUS_AI_FULL ) break; - if ( osAiSetNextBuffer( buffer, length ) == -1 ) break; - aiFIFOsamples = length >> PCM_BYTES_PER_SAMPLE_SHIFT; - if ( ! audio_started ) { - /* - * Audio playback has begun. - * Here the time count starts to synchronize audio and video - */ - audio_started = 1; - tkClockStart(); - } - if ( ++audioRingRead == AUDIO_RING_BUFFER_SIZE ) audioRingRead = 0; - --audioRingCount; - } - osSetThreadPri( NULL, (OSPri)TIMEKEEPER_PRIORITY ); - } - - if ( ! audio_done && audioRingCount < AUDIO_RING_BUFFER_SIZE && pcmBufferCount < NUM_PCMBUFs ) { - u32 samples; - u32 length; - void *buffer; - s16 *sp, *dp; - int i; + if ( ++audioRingRead == AUDIO_RING_BUFFER_SIZE ) audioRingRead = 0; + --audioRingCount; + } + osSetThreadPri( NULL, (OSPri)TIMEKEEPER_PRIORITY ); + } + /* + * Prepare the next PCM data + */ + if ( ! audio_done && audioRingCount < AUDIO_RING_BUFFER_SIZE && pcmBufferCount < NUM_PCMBUFs ) { + u32 samples; + u32 length; + void *buffer; + s16 *sp, *dp; + int i; - samples = audioproc( &gAiBuffers[next_pcmbufno][pcm_mod_samples< 0 ) { - ++pcmBufferCount; + if ( samples > 0 ) { + ++pcmBufferCount; - sp = pcmModBuf; - dp = (s16 *)((u8 *)gAiBuffers[next_pcmbufno]); - i = pcm_mod_samples << PCM_CHANNELS_SHIFT; - while ( i-- ) *dp++ = *sp++; - samples += pcm_mod_samples; - samples -= (pcm_mod_samples = samples & (PCM_ALIGN-1)); - length = samples << PCM_BYTES_PER_SAMPLE_SHIFT; - buffer = gAiBuffers[next_pcmbufno]; - osWritebackDCache( buffer, length ); - audioRing[audioRingWrite].buf = buffer; - audioRing[audioRingWrite].len = length; - if ( ++audioRingWrite == AUDIO_RING_BUFFER_SIZE ) audioRingWrite = 0; - ++audioRingCount; + sp = pcmModBuf; + dp = (s16 *)((u8 *)gAiBuffers[next_pcmbufno]); + i = pcm_mod_samples << PCM_CHANNELS_SHIFT; + while ( i-- ) *dp++ = *sp++; + samples += pcm_mod_samples; + samples -= (pcm_mod_samples = samples & (PCM_ALIGN-1)); + length = samples << PCM_BYTES_PER_SAMPLE_SHIFT; + buffer = gAiBuffers[next_pcmbufno]; + osWritebackDCache( buffer, length ); + audioRing[audioRingWrite].buf = buffer; + audioRing[audioRingWrite].len = length; + if ( ++audioRingWrite == AUDIO_RING_BUFFER_SIZE ) audioRingWrite = 0; + ++audioRingCount; - sp = (s16 *)((u8 *)(gAiBuffers[next_pcmbufno]) + length); - dp = pcmModBuf; - i = pcm_mod_samples << PCM_CHANNELS_SHIFT; - while ( i-- ) *dp++ = *sp++; + sp = (s16 *)((u8 *)(gAiBuffers[next_pcmbufno]) + length); + dp = pcmModBuf; + i = pcm_mod_samples << PCM_CHANNELS_SHIFT; + while ( i-- ) *dp++ = *sp++; - if ( ++next_pcmbufno >= NUM_PCMBUFs ) next_pcmbufno = 0; - } - else audio_done = 1; - } + if ( ++next_pcmbufno >= NUM_PCMBUFs ) next_pcmbufno = 0; + } + else audio_done = 1; + } - //! Push audio - - //! Prepare audio - - if (osRecvMesg(&tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_NOBLOCK) == 0) { - video_done = 1; - } + if ( osRecvMesg( &tkCmdMesgQ, (OSMesg *)&cmd, OS_MESG_NOBLOCK ) == 0 ) + { + /* + * tkStop() or tkStart() has been executed + * + * Set 1 in video_done and end this playback loop + * after playback of the current movie's video and + * and audio has completely finished. + */ + video_done = 1; + } } } -void createTimekeeper(void) { +/*********************************************************************** + * + * void createTimekeeper(void) + * + * Explanation + * Creates and starts the timekeeper thread + * + ***********************************************************************/ +void +createTimekeeper(void) +{ osCreateMesgQueue( &tkCmdMesgQ, &tkCmdMesgBuf, 1 ); osCreateMesgQueue( &tkResMesgQ, &tkResMesgBuf, 1 ); osCreateThread( &tkThread, TIMEKEEPER_THREAD_ID, timekeeperProc, @@ -388,7 +432,27 @@ void createTimekeeper(void) { osStartThread( &tkThread ); } -void tkStart( tkRewindProc rewind, u32 samples_per_sec ) { +/*********************************************************************** + * + * void tkStart(tkRewindProc rewind, u32 samples_per_sec) + * + * Argument + * rewind Pointer to movie rewind callback function + * + * Explanation + * Directs the timekeeper to play a new movie. Playback of the + * movie starts after callback of "rewind" by the timekeeper. After + * this, the HVQM2 video records are read and decoded and the + * completed frame buffers are handed over to the timekeeper by + * tkPushVideoframe() in succession to play the video part of the movie. + * As for the audio, the timekeeper itself calls the callback + * function (the value returned by rewind) that gets the PCM data as + * needed to playback the audio. + * + ***********************************************************************/ +void +tkStart( tkRewindProc rewind, u32 samples_per_sec ) +{ TKCMD tkcmd; tkcmd.samples_per_sec = samples_per_sec; tkcmd.rewind = rewind; @@ -396,12 +460,53 @@ void tkStart( tkRewindProc rewind, u32 samples_per_sec ) { osRecvMesg( &tkResMesgQ, (OSMesg *)NULL, OS_MESG_BLOCK ); } -void tkStop( void ) { +/*********************************************************************** + * + * void tkStop(void) + * + * Explanation + * Stops the timekeeper. However, the timekeeper continues to + * run until playback of the audio of the playing movie is complete. + * To stop the stream in mid-stream, make 0 the value returned by + * the callback function that reads audio and is called from the + * timekeeper, and then execute tkStop(). + * + * To immediately start the playback of a new movie, it is OK to + * to execute tkStart() without executing tkStop(). + * + ***********************************************************************/ +void +tkStop( void ) +{ osSendMesg( &tkCmdMesgQ, (OSMesg *)NULL, OS_MESG_BLOCK ); osRecvMesg( &tkResMesgQ, (OSMesg *)NULL, OS_MESG_BLOCK ); } -void tkPushVideoframe( void *vaddr, u32 *statP, u64 disptime ) { +/*********************************************************************** + * + * void tkPushVideoframe(void *vaddr, u32 *statP, u64 disptime) + * + * Argument + * vaddr Frame buffer address + * statP Pointer to state flag data regarding vaddr + * disptime Display time + * + * Explanation + * Entrusts the completed frame buffer vaddr to the timekeeper. + * The timekeeper displays the frame buffer after waiting for the + * "disptime" amount of time to pass from the start of the movie. + * + * To wait for the display of the vaddr frame buffer or to + * indicate that something is being displayed, the CFB_SHOWING + * flag is set in the u32 type data pointed to by the statP pointer. + * At the time when display of vaddr is finished, (once its display + * is over and display has switched to another frame buffer) this + * flag is cleared by the timekeeper. + * + ***********************************************************************/ +void +tkPushVideoframe( void *vaddr, u32 *statP, u64 disptime ) +{ *statP |= CFB_SHOWING; while ( videoRingCount >= VIDEO_RING_SIZE ) osYieldThread(); videoRing[videoRingWrite].disptime = disptime;