It is currently Wed Jun 20, 2018 12:12 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 106 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8  Next
Author Message
PostPosted: Fri Apr 17, 2015 12:59 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
Tsutarja wrote:
So, when the carry flag is set, SBC #$00 subtracts nothing, but if it's cleared, it will subtract #$01 instead?

Exactly. Whenever the carry is clear it will subtract one extra unit.

This is why we need to use SEC before subtractions, we don't want to accidentally subtract one more than we should from the first byte, we only want to do that to the other bytes, to propagate any possible borrow.

Quote:
If this is right, does this work with ADC too?

Yes, the carry affects additions too, but in the opposite way. Whenever it's set, it adds one extra unit.

Again, this is why we use CLC before additions, we don't want to add one more than the actual number we're adding, we only want to propagate the carry to the other places.

Quote:
So, they point the locations of the tables in ROM?

Yes, exactly like in your example.

The update routine itself sets the ScreenPointer and the MetatilePointer as necessary every time it's called, but you have to set LevelPointer whenever you start a new level.


Top
 Profile  
 
PostPosted: Sat Apr 18, 2015 9:42 am 
Offline
User avatar

Joined: Sun Oct 12, 2014 11:06 am
Posts: 123
Location: Finland
Okay, I've been trying to think of how to use the decoding subroutine as a loop to draw a screen, but it has been quiet difficult to think of a good way to make it work. I probably could make it work, but it would be very slow. I want to have the individual screen loader as a subroutine in a way that I can use it anytime by giving the address of the screen. If possible to have it so that it can draw both nametables. I guess column per frame is not too much to overflow the buffer. How should be doing this? (I don't know if this is simple or not, but I may be thinking too complicated again)

_________________
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi


Top
 Profile  
 
PostPosted: Sat Apr 18, 2015 2:11 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
If you're doing things as I suggested, drawing the initial screen is as simple as this:

Code:
   sec
   lda CameraX+0
   sbc #$20
   sta ColumnX+0
   lda CameraX+1
   sbc #$00
   sta ColumnX+1
   lda #$50 ;10 columns * 8 metatiles
   sta Counter
Loop:
   jsr DecodeMetatile
   ;OMITTED: WRITE BUFFER TO VRAM
   dec Counter
   beq Done
   clc
   lda ColumnX+0
   adc #$04
   sta ColumnX+0
   lda ColumnX+1
   adc #$00
   sta ColumnX+1
Done:

You can't possibly think that reimplementing the entire decoding proccess is simpler than this loop.

If you make the VRAM updating its own subroutine, the omitted part is just another JSR. You'd use the same routine in the NMI. Subroutines are your friends, so don't repeat yourself unless you absolutely need to.


Top
 Profile  
 
PostPosted: Sun Apr 19, 2015 2:31 am 
Offline
User avatar

Joined: Sun Oct 12, 2014 11:06 am
Posts: 123
Location: Finland
tokumaru wrote:
Code:
   sec
   lda CameraX+0
   sbc #$20
   sta ColumnX+0
   lda CameraX+1
   sbc #$00
   sta ColumnX+1
   lda #$50 ;10 columns * 8 metatiles
   sta Counter
Loop:
   jsr DecodeMetatile
   ;OMITTED: WRITE BUFFER TO VRAM
   dec Counter
   beq Done
   clc
   lda ColumnX+0
   adc #$04
   sta ColumnX+0
   lda ColumnX+1
   adc #$00
   sta ColumnX+1
Done:



I just tested the code, but it seems like the SBC #$00 causes ColumnX+1 to underflow to #$FF (because camera position is #$00 on bot X and Y axis) and not drawing the screen because it's larger than the level length. According to FCEUX's debugger after the screen draw routine finished, the game goes to loop the IRQ routine infinitely for some reason (Interrupt disable flag is set so it shouldn't happen). I tried removing the SBCs, but then the ADCs will mess something up (at least it seems like it as far as my debugging skills goes), and the loop obviously doesn't work without them. I did try setting the camera position to #$20 to prevent the carry flag getting cleared and the ColumnX+1 getting underflowed, but that didn't help. I also did take the background update routine from NMI as a separate subroutine so I can use it at the omitted part like you suggested.

_________________
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi


Top
 Profile  
 
PostPosted: Sun Apr 19, 2015 5:59 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
Hum... it's hard to debug this not knowing how things are organized in the ROM, but this loop is supposed to run before the game loop, with rendering turned off. You also don't want the NMI meddling with thing while this runs, so make sure to configure flags as necessary to keep the NMI handler from doing anything PPU related.

The Camera must be positioned already, as does the pointer to the level and the level length, all used by the DecodeMetatile subroutine. This is all I can think of at the moment, but look hard for anything that could be interfering with the variables used here.

If you want me to take a look at the ROM, I can.

The underflow you mentioned is expected, which is why we check whether the column is within the boundaries of the level inside the DecodeMetatile routine. ColumnX will be out of bounds for the first few iterations of the loop, due to CameraX being 0, but since ColumnX is incremented each iteration, it will eventually be within bounds.

Does the normal column rendering during scrolling work? Since you're reusing code from that, you must make sure that works properly.


Top
 Profile  
 
PostPosted: Sun Apr 26, 2015 11:13 am 
Offline
User avatar

Joined: Sun Oct 12, 2014 11:06 am
Posts: 123
Location: Finland
I think I'm going to make the sound engine next. How difficult would it be to implement "instruments" that have pre-defined volume and pitch envelopes? I think that would save some space over writing the envelopes in the song data.

_________________
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi


Top
 Profile  
 
PostPosted: Sun Apr 26, 2015 11:44 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20160
Location: NE Indiana, USA (NTSC)
Most NES games' music engines use some sort of "instrument" to represent duty, volume, and pitch changes. Play around in FamiTracker to see one common method.


Top
 Profile  
 
PostPosted: Sun Apr 26, 2015 2:06 pm 
Offline
User avatar

Joined: Fri Jan 24, 2014 9:05 am
Posts: 147
Location: Hungary
Tsutarja wrote:
I think I'm going to make the sound engine next. How difficult would it be to implement "instruments" that have pre-defined volume and pitch envelopes? I think that would save some space over writing the envelopes in the song data.

There are really only two options that come to my mind:
1. Implementing some sort of ADSR envelope in software, therefore calculating your volume and other changes with countdowns and selectable load values for these counters (Capcom games use this method)
2. Making a bunch of tables with all the raw values inteded to be consequently written to $4000 and $4004, and then writing the next item one after the other until the stream is terminated or halted, until the engine signals that it has read a note cut byte, which then runs the "release" part of the instrument.

As for pitch, you have to apply the deviation to the low period value once you have it, and you can also store this as a data stream. I do my vibrato in a cheap way (I guess) by using 4 different 16-byte long sequences, all containing a stream of signed relative deviations from the previous value. I can simply select which sequence to use by changing the upper 4 bits of my variable, the lower 4 bits are loaded into Y, and I wrap these bits back to 0 when they overflow.
Plus it also has a countdown from the start of the note, so I can make it automatically apply mid-note or whenever I want.
(I realise this is really out of context so it doesn't make much sense but hopefully you can see what I meant)

Code:
  VibHandler:
  ; apply vibrato to a pulse channel
   lda pu1_vibenable,x
   bmi +thereisvib ; if the vibrato is disabled, don't waste time with this
   rts

   +thereisvib:
   bit temp_0
   bvc +read ; keyon frame has to initialize the value reading vector
   ldy temp_6 ; store 4 high bits for the value reading
   lda pu1_vibtbl,y
   sta pu1_vibphase,x
   +read:
   ldy pu1_vibphase,x
   lda Vibrato_TBL,y
   sta pu1_vibvalue,x
   tya
   and #$0F ; if all 16 items are done, loop back to the first
   cmp #$0F
   bne +notatend
   tya
   and #$F0
   sta pu1_vibphase,x
   jmp +here ; skip increment here
   
   +notatend:
   inc pu1_vibphase,x
   +here:
   lda pu1_vibtimeleft,x
   beq +timeover
   dec pu1_vibtimeleft,x ; timer is nonzero so no vibrato applies yet
   rts
   
   +timeover:
   lda temp_6
   asl a
   asl a
   tay
   lda pu1_loSHmu,y
   clc
   adc pu1_vibvalue,x ; add relative sine to the low period
   sta pu1_loSHmu,y
   rts

 Vibrato_TBL: ; contains relative waves for modulation
  .db $01,$01,$00,$01,$00,$00,$FF,$00,$FF,$FE,$FF,$00,$FF,$00,$01,$02  ; 16/60 Hz Depth 3
  .db $02,$01,$00,$FF,$FE,$FE,$FF,$03,$02,$01,$00,$FF,$FE,$FE,$FF,$03 ; 8/60 Hz  Depth 3
  .db $03,$FD,$FD,$03,$03,$FD,$FD,$03,$03,$FD,$FD,$03,$03,$FD,$FD,$03 ; 4/60 Hz  Depth 3
  .db $02,$02,$01,$FE,$FD,$FE,$FF,$03,$02,$02,$01,$FE,$FD,$FE,$FF,$03  ; 8/60 Hz  Depth 5


Top
 Profile  
 
PostPosted: Sun Apr 26, 2015 2:58 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20160
Location: NE Indiana, USA (NTSC)
za909 wrote:
There are really only two options [for defining envelopes] that come to my mind:
1. Implementing some sort of ADSR envelope in software, therefore calculating your volume and other changes with countdowns and selectable load values for these counters (Capcom games use this method)
2. Making a bunch of tables with all the raw values inteded to be consequently written to $4000 and $4004, and then writing the next item one after the other until the stream is terminated or halted, until the engine signals that it has read a note cut byte, which then runs the "release" part of the instrument.

NerdTracker uses mostly 1, and FamiTracker uses all 2. My own music engine uses a mix of the two: tables at 2 bytes per frame for the attack and early decay, and then a simple volume ramp for the late decay and sustain. This technique of data-heavy attacks and parameter-driven sustains was inspired by Linear Arithmetic synthesis, a technique used in Roland's D-50 synthesizer that combines data-heavy PCM attack transients with parameter-driven digital subtractive synthesis for the rest of the note.


Top
 Profile  
 
PostPosted: Sun Apr 26, 2015 11:59 pm 
Offline
User avatar

Joined: Sun Oct 12, 2014 11:06 am
Posts: 123
Location: Finland
I think I'm going with having streams of volume duty and pitch data that are read every frame. #$00 would be no volume or pitch envelope, so it's skipped. Duty would be assigned to the notes in instrument, but can be changed with the duty stream if necessary. I don't think that I'll be needing values higher than #$0F for the envelopes, so I could use the upper bits to set some parameters, like add or subtract for pitch and loop flag that would instead of changing the values, would jump X amount of bytes back in the stream and read that instead. I attached one of the songs I've made for the game so you get some kind of idea what kind of style I use (I don't use instruments in FamiTracker because for me it's easier to read when volume, pitch, etc are visible).

Attachment:
Kemono Boss 900 BPM.ftm [13.87 KiB]
Downloaded 56 times


EDIT: Here is how I was planning of making the instruments and envelopes:

Code:
InstrumentSQ_Duty0:
 .db %00110000         ; Duty 12.5%, Length Counter Halt, Constant Volume

InstrumentSQ_Duty1:
 .db %01110000         ; Duty 25%, Length Counter Halt, Constant Volume

InstrumentSQ_Duty2:
 .db %10110000         ; Duty 50%, Length Counter Halt, Constant Volume

InstrumentSQ_Duty3:
 .db %11110000         ; Duty 75%, Length Counter Halt, Constant Volume

InstrumentNoise:
 .db %00110000

 ; %xxx1 xxxx 0 = Add, 1 = Subtract (Pitch only) ($10)
 ; %xx1x LLLL Loop Flag, Subtract 'LLLL' from envelope stream counter  ($2L)
 ; %x1xx WWWW Wait 'WWWW' frames and move on to the next byte ($4W)
 ; %1xxx xxxx Halt Flag, Stop envelope update and leave last updated value ($80)

            ; Square Volume Envelopes

SQVolEnv00:
 .db $07,$07,$07,$06,$06,$05,$80

SQVolEnv01:
 .db $02,$02,$02,$01,$80

SQVolEnv02:
 .db $06,$06,$07,$80

SQVolEnv03:
 .db $02,$02,$02,$00,$00,$01,$80

SQVolEnv04:
 .db $06,$06,$07,$01,$80

SQVolEnv05:
 .db $04,$04,$03,$01,$80

SQVolEnv06:
 .db $02,$02,$03,$01,$80

            ; Noise Volume Envelopes

NoiseVolEnv00:
 .db $05,$05,$03,$03,$01,$00,$80

NoiseVolEnv01:
 .db $03,$03,$02,$02,$01,$00,$80

            ; Square Pitch Envelopes

SQPtcEnv00:
 .db $43,$14,$00,$07,$00,$17,$00,$24

SQPtcEnv01:
 .db $46,$14,$00,$00,$07,$00,$00,$00,$17,$00,$00,$27

SQPtcEnv02:
 .db $4F,$4B,$14,$00,$00,$07,$00,$00,$00,$17,$00,$00,$27

_________________
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi


Top
 Profile  
 
PostPosted: Tue Apr 28, 2015 1:51 am 
Offline
User avatar

Joined: Sun Oct 12, 2014 11:06 am
Posts: 123
Location: Finland
I think I have the data format ready. Do you think that this would be too complex/slow to process, and can it be simplified?

I included only Square 1 channel to not to make the post too long.

Code:
            ; Channels in track

MusicBoss:
 .dw BossSQ1,BossSQ2,BossTri,BossNoise,BossDMC

            ; Patterns in channel

BossSQ1:
 .dw BossSQ1_00,BossSQ1_00
 .dw BossSQ1_01,BossSQ1_02,BossSQ1_03,BossSQ1_04
 .dw BossSQ1_01,BossSQ1_02,BossSQ1_03,BossSQ1_04
 .dw BossSQ1_05,BossSQ1_06,BossSQ1_05,BossSQ1_07

            ; Streams in Pattern

BossSQ1_00:
 .dw BossSQ1_00_Note,BossSQ1_00_Volume,BossSQ1_00_Vibrato
 .dw BossSQ1_00_Duty,BossSQ1_00_Len_Lo,BossSQ1_00_Len_Hi

BossSQ1_01:
 .dw BossSQ1_01_Note,BossSQ1_01_Volume,BossSQ1_01_Vibrato
 .dw BossSQ1_01_Duty,BossSQ1_01_Len_Lo,BossSQ1_01_Len_Hi

BossSQ1_02:
 .dw BossSQ1_02_Note,BossSQ1_02_Volume,BossSQ1_02_Vibrato
 .dw BossSQ1_02_Duty,BossSQ1_02_Len_Lo,BossSQ1_02_Len_Hi

BossSQ1_03:
 .dw BossSQ1_03_Note,BossSQ1_03_Volume,BossSQ1_03_Vibrato
 .dw BossSQ1_03_Duty,BossSQ1_03_Len_Lo,BossSQ1_03_Len_Hi

BossSQ1_04:
 .dw BossSQ1_04_Note,BossSQ1_04_Volume,BossSQ1_04_Vibrato
 .dw BossSQ1_04_Duty,BossSQ1_04_Len_Lo,BossSQ1_04_Len_Hi

BossSQ1_05:
 .dw BossSQ1_05_Note,BossSQ1_05_Volume,BossSQ1_05_Vibrato
 .dw BossSQ1_05_Duty,BossSQ1_05_Len_Lo,BossSQ1_05_Len_Hi

BossSQ1_06:
 .dw BossSQ1_06_Note,BossSQ1_06_Volume,BossSQ1_06_Vibrato
 .dw BossSQ1_06_Duty,BossSQ1_06_Len_Lo,BossSQ1_06_Len_Hi

BossSQ1_07:
 .dw BossSQ1_07_Note,BossSQ1_07_Volume,BossSQ1_07_Vibrato
 .dw BossSQ1_07_Duty,BossSQ1_07_Len_Lo,BossSQ1_07_Len_Hi


And here is example of one pattern's streams:

Code:
BossSQ1_00_Note:
 .db B_2,As2,A_2,Gs2            ; Notes represent a value from range of $00 - $5E
 .db B_2,As2,A_2,Gs2            ; This value is used in X or Y incremented addressing
 .db B_2,As2,A_2,Gs2            ; to get the correct period value for the note
 .db B_2,As2,A_2,Gs2

BossSQ1_00_Volume:
 .db SQVolEnv00,SQVolEnv00,SQVolEnv00,SQVolEnv00           ; Volume, pitch and duty envelopes are in my previous post
 .db SQVolEnv00,SQVolEnv00,SQVolEnv00,SQVolEnv00
 .db SQVolEnv00,SQVolEnv00,SQVolEnv00,SQVolEnv00
 .db SQVolEnv00,SQVolEnv00,SQVolEnv00,SQVolEnv00

BossSQ1_00_Vibrato:
 .db SQPtcEnv00,SQPtcEnv00,SQPtcEnv00,SQPtcEnv00
 .db SQPtcEnv00,SQPtcEnv00,SQPtcEnv00,SQPtcEnv00
 .db SQPtcEnv00,SQPtcEnv00,SQPtcEnv00,SQPtcEnv00
 .db SQPtcEnv00,SQPtcEnv00,SQPtcEnv00,SQPtcEnv00

BossSQ1_00_Duty:
 .db SQ_Duty1,SQ_Duty1,SQ_Duty1,SQ_Duty1
 .db SQ_Duty1,SQ_Duty1,SQ_Duty1,SQ_Duty1
 .db SQ_Duty1,SQ_Duty1,SQ_Duty1,SQ_Duty1
 .db SQ_Duty1,SQ_Duty1,SQ_Duty1,SQ_Duty1

BossSQ1_00_Len_Lo:
 .db $06,$07,$06,$07
 .db $06,$07,$06,$07
 .db $06,$07,$06,$07
 .db $06,$07,$06,$07

BossSQ1_00_Len_Hi:
 .db $00,$00,$00,$00                 ; I have these just in case
 .db $00,$00,$00,$00                 ; Not sure if I'll ever need these
 .db $00,$00,$00,$00
 .db $00,$00,$00,$00

BossSQ1_01_Note:
 .db B_2,Cs3,D_3,Fs3,E_3,D_3,A_2

BossSQ1_01_Volume:
 .db SQVolEnv02,SQVolEnv02,SQVolEnv02,SQVolEnv02,SQVolEnv02,SQVolEnv02,SQVolEnv02

BossSQ1_01_Vibrato:
 .db SQPtcEnv00,SQPtcEnv00,SQPtcEnv01,SQPtcEnv01,SQPtcEnv01,SQPtcEnv01,SQPtcEnv01

BossSQ1_01_Duty:
 .db SQ_Duty1,SQ_Duty1,SQ_Duty1,SQ_Duty1,SQ_Duty1,SQ_Duty1,SQ_Duty1

BossSQ1_01_Len_Lo:
 .db $06,$07,$0D,$13,$14,$1A,$0C

BossSQ1_01_Len_Hi:
 .db $00,$00,$00,$00,$00,$00,$00

BossSQ1_02_Note:
 .db B_2

BossSQ1_02_Volume:
 .db SQVolEnv02

BossSQ1_02_Vibrato:
 .db SQPtcEnv02

BossSQ1_02_Duty:
 .db SQ_Duty1

BossSQ1_02_Len_Lo:
 .db $68

BossSQ1_02_Len_Hi:
 .db $00

_________________
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi


Top
 Profile  
 
PostPosted: Thu Apr 30, 2015 3:26 pm 
Offline
User avatar

Joined: Fri Jan 24, 2014 9:05 am
Posts: 147
Location: Hungary
You're on the right track but by having looked at your plans, I can't help advising you to find some other way of representing your note effects because this produces TONS of redundant data, for example you are storing the current instrument for every single note. Instead, you should keep a variable like "Pu1Instr" or something, use that as an index to find the table representing this instrument number, and having an effect command to change the Pu1Instr variable. This way you will only have two bytes added when you actually need to switch to different instrument.
I remember in my earlier sound engine I did this by setting up an indirect vector to the desired table, then loading the current volume write number into Y and reading from the table. (Sorry, couldn't find my source code for that)
As you can see I only had 16 pulse instruments, the other bits of p1_flags were bitflags for detune, hw. sweep mode, etc.

Code:
  GetInstrumentId:
; Put instrument ID in Y for index

   lda p1_flags,x
   and #%00001111
   tay   
   rts

Code:
; Y is Instrument ID
   sty temp_4
   tya
   asl a
   tay
   lda instrumentaddrTBL,y
   sta temp_E
   lda instrumentaddrTBL+1,y
   sta temp_F
   ldy p1_patchseq,x
   lda (temp_E),y
   sta p1_shvol,x
   ldy temp_4
   rts


Top
 Profile  
 
PostPosted: Sun May 03, 2015 10:15 pm 
Offline
User avatar

Joined: Sun Oct 12, 2014 11:06 am
Posts: 123
Location: Finland
za909 wrote:
Instead, you should keep a variable like "Pu1Instr" or something, use that as an index to find the table representing this instrument number, and having an effect command to change the Pu1Instr variable. This way you will only have two bytes added when you actually need to switch to different instrument.


That actually sounds better. I'll have to edit the data later today.

By the way, when I start to actually write the sound engine, which one of these would be better:
1. Run the whole sound engine in NMI
2. Run sound engine outside NMI and copy bytes to the registers during NMI

I have heard both of these methods, but I'm not sure which one is better and what are their possible downsides.

_________________
UP SIDE DOWN A B A B B A B A Hidari migi
L R L R STOP & DASH & UP & TALK Ijou nashi


Top
 Profile  
 
PostPosted: Mon May 04, 2015 5:47 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10513
Location: Rio de Janeiro - Brazil
Tsutarja wrote:
2. Run sound engine outside NMI and copy bytes to the registers during NMI

This approach makes more sense for PPU updates, since access to VRAM is restricted, but the APU can be accessed at any time, so there's no problem in doing everything at once.

The only advantage I can see in buffering APU updates is that it might make the timing a little steadier, since the time taken to process the audio data won't delay the register writes, but unless your sound engine's execution times vary greatly, I doubt the difference is noticeable.


Top
 Profile  
 
PostPosted: Tue May 05, 2015 6:06 am 
Offline
User avatar

Joined: Fri Jan 24, 2014 9:05 am
Posts: 147
Location: Hungary
By the way, if you are struggling with DPCM samples and the space loss they cause in the fixed bank, you can also try playing your code as a sound! Most of the time it's just random garbage (but you can still use it as a looped wind sound so your noise channel is free to do something else) but I found that an unrolled PLA STA $2007 loop conveniently produces a C note sound with a very wavetable-esque tone to it. I made a little happy loop with it in Famitracker to show what it sounds like but my engine could do it as well if I spent an hour or two with it. (If you are working with CHR-RAM you can also show animated tiles made of code, sometimes you can find a useful flashing tile or something like that)

Edit: As far as I know, Rockman 4 MI uses this method and plays some garbled sounds together with the noise channel.


Attachments:
nmisampletest.ftm [4.61 KiB]
Downloaded 46 times
Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 106 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group