base:fake_music_player
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | base:fake_music_player [2015-04-17 04:31] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Fake Music Player ====== | ||
+ | By karoshier. | ||
+ | |||
+ | ===== The problem ===== | ||
+ | While coding a game, or a demo effect that eats up lots of raster time, the coder ought to consider also the music. And such music is likely not to be ready yet or simply the code is in a too early stage to start talking about the music which is going to fulfill the explosion of senses of the finished production. But we still want: | ||
+ | |||
+ | * our init and play calls already in place, to forget about them | ||
+ | * the memory space to be already allocated, to verify that code, graphics and music fit in the allotted space | ||
+ | * raster time to be randomly eaten, to verify that we still have enough left and our VIC sync routine works | ||
+ | |||
+ | In the past I have always solved this problem quick and dirty by including some random tune in my code which | ||
+ | would later get replaced by the real one. But the size taken by a random tune cannot be configured. Also, the amount of raster time used cannot be configured. And finding a ready-made tune in our archive, that loads at the bizarre address we want it to, requires some time which we would rather not invest. If we plan to have multiple tunes in the production, then we want to be able to test what happens when we change the tune. Finally, when the tune arrives, it has to merge in seamlessly and we need to remember removing all our debug code, like for example the always useful inc/dec $D020. | ||
+ | |||
+ | ===== The solution ===== | ||
+ | The solution listed below solves all the above problems, as it can be configured to fit our needs and reduces to almost zero the time we need to invest in messing with the music. | ||
+ | The code basically configures the SID for total silence, but still turns the volume to its maximum so that the typical " | ||
+ | Voice 3 will have its oscillator running, but the voice itself will be silenced using the bit 3OFF in $D418. | ||
+ | The waveform of this oscillator gets read from $D41B (OSC3/ | ||
+ | Also, there are some other features that better emulate the presence of a real music player. | ||
+ | |||
+ | ===== How to configure the code ===== | ||
+ | There are some constants at the beginning which we can abuse to configure the fake player. | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | The code also defines two labels that can be used in our code: '' | ||
+ | Different tunes are selectable, from 0 to 3. They behave slightly differently, | ||
+ | |||
+ | ===== The code ===== | ||
+ | < | ||
+ | ; | ||
+ | ; | ||
+ | ; Music binary wrapper | ||
+ | ; with fake debug player | ||
+ | ; by Karoshier / DaCapo | ||
+ | ; | ||
+ | ;This module has been designed to work with ACME, but there is nothing in it | ||
+ | ;which can't be adapted to other assemblers. | ||
+ | ; | ||
+ | ;This module imports a music using the !binary directive (see end of file). | ||
+ | ;It provides init and play addresses as labels for external code to more | ||
+ | ; | ||
+ | ;If you set to 1 the MUSIC_NOT_AVAILABLE label, then the assembler will not | ||
+ | ;attempt to load the music. The space that should be used by the music will be | ||
+ | ;instead used by a routine which fakes the presence of a music in several | ||
+ | ;respects. | ||
+ | ;- The initialization routine will silence the SID | ||
+ | ;- The module will allocate MUSIC_FAKE_SIZE space for itself, thereby posing | ||
+ | ; size problems exactly the same way a music player would do | ||
+ | ;- The play routine will take a random amount of time to execute, between a | ||
+ | ; programmable minimum and maximum limit expressed in raster lines | ||
+ | ;- A pointer in zero page will be used as a real player routine would do | ||
+ | ; | ||
+ | ; | ||
+ | |||
+ | ;***** Module configuration **************************************************** | ||
+ | MUSIC_START_ADDRESS = $1800 ; | ||
+ | MUSIC_NOT_AVAILABLE = 1 ;If set to 1, the fake routine will be used instead of loading the music binary | ||
+ | MUSIC_FAKE_SHOW_RASTER = 1 ;If set to 1, $D020 will be changed to show the raster time taken by the fake player | ||
+ | MUSIC_FAKE_SIZE = $1800 ;Size you wish to allocate to the music | ||
+ | MUSIC_FAKE_MIN_EXEC_TIME = $07 ;Minimum number of raster lines used by the playback routine | ||
+ | MUSIC_FAKE_MAX_EXEC_TIME = $12 ;Maximum number of raster lines used by the playback routine | ||
+ | MUSIC_FAKE_ZERO_PAGE_PTR = $FE ;Location of the zero page pointer that this routine will mess up | ||
+ | |||
+ | ;***** Constants *************************************************************** | ||
+ | ;You are not supposed to need changing these constants | ||
+ | MUSIC_FAKE_OSC_EXEC_TIME = MUSIC_FAKE_MAX_EXEC_TIME - MUSIC_FAKE_MIN_EXEC_TIME | ||
+ | |||
+ | ;***** Starting point management *********************************************** | ||
+ | !if (*> | ||
+ | | ||
+ | } | ||
+ | |||
+ | MUSIC_INIT = MUSIC_START_ADDRESS | ||
+ | MUSIC_PLAY = MUSIC_INIT + 3 ; | ||
+ | |||
+ | *=MUSIC_START_ADDRESS | ||
+ | |||
+ | !if (MUSIC_NOT_AVAILABLE=1) { | ||
+ | ;***** Fake music player ******************************************************* | ||
+ | jmp MUSIC_FAKE_INIT | ||
+ | jmp MUSIC_FAKE_PLAY | ||
+ | ;----- Initialization routine -------------------------------------------------- | ||
+ | ; | ||
+ | ;registers like a real music player would do. | ||
+ | ; | ||
+ | ; | ||
+ | ; A = Tune number | ||
+ | ;Returns: | ||
+ | ; < | ||
+ | ;Uses: | ||
+ | ; A, X, Y | ||
+ | MUSIC_FAKE_INIT: | ||
+ | and #$03 ;Ensure our "song number" | ||
+ | tax ;Use the song number as index | ||
+ | lda #$8F ;Set volume to maximum but silence voice 3 | ||
+ | sta $D418 | ||
+ | lda #$00 ;Shut up the sid | ||
+ | sta $D415 ;Kill resonance and filter settings | ||
+ | sta $D416 | ||
+ | sta $D417 | ||
+ | ;Silence voices 1 and 2 | ||
+ | ldy #$0D ;Clean registers from $D400 to $D40D, ... | ||
+ | MUSIC_FAKE_INIT_LOOP: | ||
+ | sta $D400, | ||
+ | dey ;Next register | ||
+ | bpl MUSIC_FAKE_INIT_LOOP | ||
+ | lda #$08 ;Reset oscillators 1 and 2 | ||
+ | sta $D404 | ||
+ | sta $D40B | ||
+ | lda #$00 ;Remove oscillator reset condition | ||
+ | sta $D404 | ||
+ | sta $D40B | ||
+ | ;Now setup voice 3 | ||
+ | lda FREQ_TABLE_LO, | ||
+ | sta $D40E | ||
+ | lda FREQ_TABLE_HI, | ||
+ | sta $D40F | ||
+ | lda PULS_TABLE_LO, | ||
+ | sta $D410 | ||
+ | lda PULS_TABLE_HI, | ||
+ | sta $D411 | ||
+ | lda #$00 ;Clear ADSR as we don't need it | ||
+ | sta $D413 | ||
+ | sta $D414 | ||
+ | lda CTRL_TABLE, | ||
+ | sta $D412 | ||
+ | rts ; | ||
+ | |||
+ | ;----- Playback routine -------------------------------------------------------- | ||
+ | ;Acts like a real music player, taking a random time to execute, using all three | ||
+ | ;CPU registers and messing up two zero page locations. This routine reads the | ||
+ | ;value of OSC3 from $D41B to get some random numbers. Channel 3 has been | ||
+ | ;configured exactly for this purpose, but is kept silent because $D418 was | ||
+ | ;configured during init to silence voice 3. You can therefore freely use the | ||
+ | ;remainig two channels if ever needed. | ||
+ | ; | ||
+ | ; | ||
+ | ; < | ||
+ | ;Returns: | ||
+ | ; < | ||
+ | ;Uses: | ||
+ | ; A, X, Y | ||
+ | MUSIC_FAKE_PLAY: | ||
+ | !if (MUSIC_FAKE_SHOW_RASTER = 1) { | ||
+ | dec $D020 | ||
+ | } | ||
+ | lda $D41B ;Get random values to mess up the zero page pointer | ||
+ | sta MUSIC_FAKE_ZERO_PAGE_PTR+0 | ||
+ | lda $D41B | ||
+ | sta MUSIC_FAKE_ZERO_PAGE_PTR+1 | ||
+ | lda $D41B ;Get a random value defining how much time we should take to execute | ||
+ | !if (MUSIC_FAKE_OSC_EXEC_TIME < $80) { | ||
+ | lsr ; | ||
+ | } | ||
+ | !if (MUSIC_FAKE_OSC_EXEC_TIME < $40) { | ||
+ | lsr ; | ||
+ | } | ||
+ | !if (MUSIC_FAKE_OSC_EXEC_TIME < $20) { | ||
+ | lsr ; | ||
+ | } | ||
+ | !if (MUSIC_FAKE_OSC_EXEC_TIME < $10) { | ||
+ | lsr ; | ||
+ | } | ||
+ | !if (MUSIC_FAKE_OSC_EXEC_TIME < $08) { | ||
+ | lsr ; | ||
+ | } | ||
+ | !if (MUSIC_FAKE_OSC_EXEC_TIME < $04) { | ||
+ | lsr ; | ||
+ | } | ||
+ | !if (MUSIC_FAKE_OSC_EXEC_TIME < $02) { | ||
+ | lsr ; | ||
+ | } | ||
+ | !if (MUSIC_FAKE_OSC_EXEC_TIME < $01) { | ||
+ | lsr ; | ||
+ | } | ||
+ | MUSIC_FAKE_ENSURE_FIT: | ||
+ | cmp # | ||
+ | beq MUSIC_FAKE_VALUE_OK | ||
+ | bcc MUSIC_FAKE_VALUE_OK | ||
+ | sec ; | ||
+ | sbc # | ||
+ | jmp MUSIC_FAKE_ENSURE_FIT | ||
+ | MUSIC_FAKE_VALUE_OK: | ||
+ | clc ;Add a base value of time that needs to elapse anyway | ||
+ | adc # | ||
+ | tay ;Use this value to count a certain number of raster lines | ||
+ | dey ;One raster line has been already used by the above computation | ||
+ | MUSIC_FAKE_COUNT_WAIT: | ||
+ | ldx #$0B ;2 Delay execution for for approximately 63 cycles (that is one raster line) | ||
+ | MUSIC_FAKE_COUNT_LINE: | ||
+ | dex ;2 | ||
+ | bne MUSIC_FAKE_COUNT_LINE | ||
+ | nop ;2 | ||
+ | dey ; | ||
+ | bne MUSIC_FAKE_COUNT_WAIT | ||
+ | !if (MUSIC_FAKE_SHOW_RASTER = 1) { | ||
+ | inc $D020 | ||
+ | } | ||
+ | rts ; | ||
+ | |||
+ | ;----- Data tables ------------------------------------------------------------- | ||
+ | ;These tables are there to allow the caller to choose more than one " | ||
+ | ;Different tunes will behave differently regarding to how randomly they use their | ||
+ | ;raster time. Such " | ||
+ | |||
+ | ;Value that will go into the frequency register of voice 3 | ||
+ | FREQ_TABLE_LO: | ||
+ | !byte $FF, $FF, $FF, $FF | ||
+ | FREQ_TABLE_HI: | ||
+ | !byte $0F, $0F, $0F, $0F | ||
+ | |||
+ | ;Value that will go into the pulse width register of voice 3 | ||
+ | ;This only makes sense if the pulse wave is used | ||
+ | PULS_TABLE_LO: | ||
+ | !byte $00, $00, $00, $00 | ||
+ | PULS_TABLE_HI: | ||
+ | !byte $00, $08, $00, $00 | ||
+ | |||
+ | ;Value that will go into the control register | ||
+ | CTRL_TABLE: | ||
+ | !byte $81, $41, $21, $11 | ||
+ | |||
+ | ;Let's use up the remaining space so that the assembler will still report crossing | ||
+ | ;segments if needed. | ||
+ | !fill MUSIC_FAKE_SIZE-(*-MUSIC_START_ADDRESS), | ||
+ | |||
+ | } else { | ||
+ | ;Music is available, therefore load it and we're done | ||
+ | ;If your music file is raw data, then you can import it with !binary < | ||
+ | ;If your music file is a PRG file, then you can import it with !binary < | ||
+ | !binary " | ||
+ | } | ||
+ | </ |
base/fake_music_player.txt · Last modified: 2015-04-17 04:31 by 127.0.0.1