Jump to content
IGNORED

Colossal Cave Adventure diary


vprette

Recommended Posts

Valter, can you provide the code that calls the multiple levels? Perhaps there is something wrong there. Also, 32 is a power of two, so that makes it suspect as some sort of bad pointer outside a look-up table or something like that.

 

-dZ.

 

I send you the st_level.asm and, yes it's not jzintv, it's crashing with Nostalgia also

Edited by vprette
Link to comment
Share on other sites

it works perfectly until LEVEL_32, as soon I add one more level I have a rom compiled without errors that crash when launched....... this is related to jzintv? what kind of error report can I force jzintv to provide?

 

I can tell you it is not jzIntv. That is for sure.

 

If you are able to run the debugger, then turn the CPU history on with the command "H" then "R" to run the emulation, then when it crashes type "D" to "dump" the CPU history.

 

Send me the "*.hst" file it generates and I'll take a look at what happened. The attached BATCH files are the ones now distributed with P-Machinery for Windows, and it assembles with symbols and execute the emulator with the necessary debug information.

 

Copy them to the folder where you game is stored. You can then assemble from the DOS prompt like this:

 

C:\build.sh

 

Then you run the debugger like this:

C:\run.sh -d

 

To run normally, just use "run.sh"

 

-dZ.

 

batchfiles.zip

 

wait, are you speaking MACOS or windows? I work in windows

 

Well, I've learned my lesson. Those are for Windows. I should have mentioned "*.bat" not "*.sh" in my comment, though. :)

 

-dZ.

 

You extract the "*.bat" files from the ZIP file, and copy them to the "col" folder where you keep your code. Then you open a "Command" shell window and change directory to that folder:

C:\CD C:\path\to\game\folder

 

And run the batch files from there.

Link to comment
Share on other sites

Valter, can you provide the code that calls the multiple levels? Perhaps there is something wrong there. Also, 32 is a power of two, so that makes it suspect as some sort of bad pointer outside a look-up table or something like that.

 

-dZ.

 

I send you the st_level.asm and, yes it's not jzintv, it's crashing with Nostalgia also

 

jzIntv may have some bugs, though I don't know of any right now; but it is a very robust program and expressly designed for developers.

Link to comment
Share on other sites

This is strange. Looking at a history dump of the ROM when it crashes, it seems to crash during the ROM header setup. The dump is identical to another one from my own game up until the point when the EXEC is reading the copyright date. There's a test there that fails:


5014 0000 0000 0000 01FE 103C 02F1 101D ---Z--iq  MVI@ R5,R1			  604
5014 0070 0000 0000 01FE 103D 02F1 101E ---Z--iq  SWAP R1				 612
5014 7000 0000 0000 01FE 103D 02F1 101F -------q  MVI@ R1,R0			  618
FFFF 7000 0000 0000 01FE 103D 02F1 1020 ------iq  SWAP R0				 626
FFFF 7000 0000 0000 01FE 103D 02F1 1021 S------q  ANDI #$00FC,R0		  632

0000 7000 0000 0000 01FE 103D 02F1 1023 ---Z--iq  BNEQ $1003			  640

 

In address $1021, my game results in a non-zero value, while the Colossal Cave ROM does, causing it fall through and jump to address $7000 (R1). It's a fast death shortly thereafter:


0000 7000 0000 0000 01FE 103D 02F1 1025 ---Z--iq  MOVR R1,R7			  647
0000 7000 0000 0000 01FE 103D 02F1 7000 ------iq  SDBD					654
0000 7000 0000 0000 01FE 103D 02F1 7001 -----D-q  SDBD					658
0000 7000 0000 0000 01FE 103D 02F1 7002 -----D-q  SDBD					662
0000 7000 0000 0000 01FE 103D 02F1 7003 -----D-q  SDBD					666
0000 7000 0000 0000 01FE 103D 02F1 7004 -----D-q  SDBD					670
0000 7000 0000 0000 01FE 103D 02F1 7005 -----D-q  SDBD					674
0000 7000 0000 0000 01FE 103D 02F1 7006 -----D-q  HLT					 678

 

 

Perhaps Joe can shed some light?

 

Valter, did you change anything in the way the ROM is set up at the top of the main source file:

ROMSETUP 16K, 2010, "P-MACH Test", BOOT_UP, STACK_SIZE

 

I suppose it is possible that some memory has been overwritten...

 

Attached is the CPU history dump.

 

-dZ.

 

dump.hst.txt

Edited by DZ-Jay
Link to comment
Share on other sites

This is strange. Looking at a history dump of the ROM when it crashes, it seems to crash during the ROM header setup. The dump is identical to another one from my own game up until the point when the EXEC is reading the copyright date. There's a test there that fails:


5014 0000 0000 0000 01FE 103C 02F1 101D ---Z--iq  MVI@ R5,R1			  604
5014 0070 0000 0000 01FE 103D 02F1 101E ---Z--iq  SWAP R1				 612
5014 7000 0000 0000 01FE 103D 02F1 101F -------q  MVI@ R1,R0			  618
FFFF 7000 0000 0000 01FE 103D 02F1 1020 ------iq  SWAP R0				 626
FFFF 7000 0000 0000 01FE 103D 02F1 1021 S------q  ANDI #$00FC,R0		  632

0000 7000 0000 0000 01FE 103D 02F1 1023 ---Z--iq  BNEQ $1003			  640

 

In address $1021, my game results in a non-zero value, while the Colossal Cave ROM does, causing it fall through and jump to address $7000 (R1). It's a fast death shortly thereafter:


0000 7000 0000 0000 01FE 103D 02F1 1025 ---Z--iq  MOVR R1,R7			  647
0000 7000 0000 0000 01FE 103D 02F1 7000 ------iq  SDBD					654
0000 7000 0000 0000 01FE 103D 02F1 7001 -----D-q  SDBD					658
0000 7000 0000 0000 01FE 103D 02F1 7002 -----D-q  SDBD					662
0000 7000 0000 0000 01FE 103D 02F1 7003 -----D-q  SDBD					666
0000 7000 0000 0000 01FE 103D 02F1 7004 -----D-q  SDBD					670
0000 7000 0000 0000 01FE 103D 02F1 7005 -----D-q  SDBD					674
0000 7000 0000 0000 01FE 103D 02F1 7006 -----D-q  HLT					 678

 

 

Perhaps Joe can shed some light?

 

Valter, did you change anything in the way the ROM is set up at the top of the main source file:

ROMSETUP 16K, 2010, "P-MACH Test", BOOT_UP, STACK_SIZE

 

I suppose it is possible that some memory has been overwritten...

 

Attached is the CPU history dump.

 

-dZ.

 

dump.hst.txt

 

OK, it's not the copyright date, that happens before. According to the memory map, address $7000 is the ECS EXEC ROM. Whatever it is, the test results in zero, causing it to fall through and ultimately crash.

 

I don't know much about the underlying EXEC, so I'm afraid I can't help much.

 

-dZ.

Link to comment
Share on other sites

OK, it's not the copyright date, that happens before. According to the memory map, address $7000 is the ECS EXEC ROM. Whatever it is, the test results in zero, causing it to fall through and ultimately crash.

 

I don't know much about the underlying EXEC, so I'm afraid I can't help much.

 

-dZ.

 

dZ,

 

The EXEC will check for "viable ROM" at $4800 and $7000 before doing most of its initialization, and if it see ROM at either location, it will jump to it.

 

My cart.mac will detect ROM segment overflows, but only if you put "ROMEND" at the end of your program. If you don't put ROMEND at the end of your program, you could successfully assemble your program, but you may have overflowed the last segment you assembled into. That's what I think happened here: The game edged out of $5000 - $6FFF into $7000, and the pre-title-screen code sees "viable ROM" at $7000 and jumps into what's actually data.

 

 

In other news, I wrote a quick and dirty Perl script to compress the Colossal Cave Phrase Data from the OS/8 version of Colossal Cave. The entire phrase database fits in about 7K words, using the extremely simplistic scheme I mentioned this morning. I'm attaching my scripts and their output.

 

--Joe

advent.zip

Link to comment
Share on other sites

The Inty memory map is not continuous for cart ROMs. You have $5000 to $6FFF free and then you must move part of your code to $8000 to $BFFF. Have a look in your symbol table and check if labels are in the range $7000 to $7FFF.

 

You can map code at $7xxx if you are careful.

 

The EXEC checks $7000 and $4800 for "viable ROM" (which means "upper 6 bits are zero") during early bootup. If it detects viable ROM there, it'll branch there immediately. So, you can wrest control of the game boot process by putting ROM at $7000 or $4800.

 

Another strategy is to make sure a value greater than $03FF ends up at $7000, also to foil the check. That usually means you can't have code that spills directly from $6FFF to $7000, but it does mean you can map ROM at $7001 - $7FFF.

 

Now, another challenge is the fact that the ECS maps ROM at $7000. One way to handle that is to use a bankswitched ROM at $7xxx, and switch out the ECS ROM and switch in your own ROM. You don't need dynamic ROM switching during your game to make that work. In fact, in the 42K memory map, the cart.mac code tries to switch out the ECS ROMs to make room for your own game.

 

In any case, I think Valter's issue is that he's spilling into $7xxx. I think he should switch to the 42K memory map, and add some ROMSEG directives to move his data out of the very limited $5000 - $6FFF segment.

 

--Joe

  • Like 1
Link to comment
Share on other sites

Sounds like I should move over to using the cart.mac myself instead of doing it all the "hard" way.

 

I'm not using cart.mac for "Paddle Party" because I am essentially doing 4 separate games, and I have different ram "assignments" for each game.

 

For example, "Doubleball" has two ball structures, "Air Hockey" has temp vectors for collision calculators, while "Tennis in Space" has variables for scrolling. So I have different "org" statements and ram allocation for each game. I don't think cart.mac supports this approach...

Link to comment
Share on other sites

In any case, I think Valter's issue is that he's spilling into $7xxx. I think he should switch to the 42K memory map, and add some ROMSEG directives to move his data out of the very limited $5000 - $6FFF segment.

 

--Joe

 

I agree, but wouldn't cart.mac catch this? P-Mach is using it, with a very explicit ROMEND at the bottom--unless it got overwritten...

Link to comment
Share on other sites

Sounds like I should move over to using the cart.mac myself instead of doing it all the "hard" way.

 

I'm not using cart.mac for "Paddle Party" because I am essentially doing 4 separate games, and I have different ram "assignments" for each game.

 

For example, "Doubleball" has two ball structures, "Air Hockey" has temp vectors for collision calculators, while "Tennis in Space" has variables for scrolling. So I have different "org" statements and ram allocation for each game. I don't think cart.mac supports this approach...

 

Cart.mac doesn't really support that "out-of-the-box," but I do something similar in Christmas Carol. I added some macros that re-assign RAM or "truncate" RAM back to a particular address so that I can assign different blocks for the various machine states. Then cart.mac can continue allocating and validating RAM usage.

 

For example, I have a set of variables that are global to the game world, but after that, there is a block of 8-bit RAM used for game-play and level parameters, and another overlapping set used for the introduction sequences. For instance, I define the global variables normally:

 


; ======================================================
; GAME WORLD STATE
; ======================================================

; --------------------------------------
; Player State Information
; --------------------------------------
PLAYER_INFO     STRUCT  0
@@ScoreRevs     SCRATCH 1
@@Score         SCRATCH 2
@@Lives         SCRATCH 1

@@DigitCount    EQU     4
@@Digits        SCRATCH @@DigitCount
               ENDS

; --------------------------------------
; Game Engine Information
; --------------------------------------
GAME_INFO       STRUCT  0
@@State         SCRATCH 1
@@CtrlScanner   SCRATCH 2
@@CtrlDisp      SCRATCH 2
@@PsgState      SCRATCH 1
               ENDS

; --------------------------------------
; Game World Information
; --------------------------------------
WORLD_INFO      STRUCT  0
@@Level         SCRATCH 1
@@Stage         SCRATCH 1
@@Difficulty    SCRATCH 1
@@Flags         SCRATCH 1

@@NextLife      SCRATCH 2
@@ExtraCount    SCRATCH 1
               ENDS

 

Then "mark" the current RAM cursors before allocating for level parameters:


; ======================================================
; LEVEL PLAY STATE
; ======================================================
LVL_SCR_START   EQU     .SCRMEM
LVL_SYS_START   EQU     .SYSMEM

; --------------------------------------
; Level State Information
; --------------------------------------
LEVEL_INFO      STRUCT  0
@@CandyCnt      SCRATCH 1
@@SnoflkCnt     SCRATCH 1
@@Deaths        SCRATCH 1
@@Perfect       SCRATCH 1

@@PresentCnt    SCRATCH 1
@@PresentVec    SCRATCH 1

@@BonusMult     SCRATCH 1

@@SpeedDelay    SCRATCH 1
@@PlayerHits    SCRATCH 1
               ENDS

; --------------------------------------
; Level Map Information
; --------------------------------------
LEVEL_MAP       STRUCT  0
               ; Level maze data base addresses
@@vmap          SYSTEM  1
@@present       SYSTEM  1
@@btab          SCRATCH 2
@@snoflk        SCRATCH 2

               ; Effective maze dimensions
@@eff_min_y     SCRATCH 1                               ; NOTE: These are set in reverse order so that
@@eff_min_x     SCRATCH 1                               ;       we can load them from 16-bit ROM without
@@eff_max_y     SCRATCH 1                               ;       having to swap them first.
@@eff_max_x     SCRATCH 1                               ;

               ; Sprite Positions
@@elf_pos       SCRATCH 2
@@snowman_pos   SCRATCH 2
@@ghost_pos     SCRATCH 2

               ; Sprite Orientation
@@elf_dir       SCRATCH 1
@@snowman_dir   SCRATCH 1
@@ghost_dir     SCRATCH 1

               ; Bonus Item Counts
@@candy_cnt     SCRATCH 1
@@snoflk_cnt    SCRATCH 1
@@bonus_cnt     SCRATCH 1
               ENDS

 

 

 

Then I "truncate" RAM by resetting the cart.mac cursors back to my markers:


; ======================================================
; PRACTICE MODE STATE
; ======================================================
LVL_SCR_END     EQU     .SCRMEM                         ; \
LVL_SYS_END     EQU     .SYSMEM                         ;  |_ "Practice Mode" variables overlap the
RESET_SCRATCH_RAM(LVL_SCR_START)                        ;  |    "Level Play" variables
RESET_SYSTEM_RAM (LVL_SYS_START)                        ; /

; --------------------------------------
; Practice Mode State Information
; --------------------------------------
SCROLL_INFO     STRUCT  0
               ; Menu screen data base addresses
@@btab          SYSTEM  1

@@ScrollFunc    SYSTEM  1
@@ShiftFunc     SYSTEM  1
@@ScrollRate    SYSTEM  1

@@Padding       EQU     2
@@StartRate     EQU     PIX_RATE(35)                    ; Start at 20 pixels/second
@@AccelRate     EQU     PIX_RATE(25)                    ; Accelerate by (20 pixels/second)^second
@@MaxRate       EQU     PIX_RATE(400)
@@Delay         SCRATCH 1
@@State         SCRATCH 1
@@Flags         SCRATCH 1
@@Column        SCRATCH 1
@@PadCol        SCRATCH 1
@@RateFrac      SCRATCH 1
               ENDS

 

Both sets overlap, but it doesn't matter as long as they are used at different states, which my game does.

 

I love "cart.mac," mostly because it solved a lot of problems for me before I even had to get acquainted with the complexities of managing ROM segments. When that became a necessity, it was rather magical to know that cart.mac supported that too!

 

-dZ.

Link to comment
Share on other sites

OK, it's not the copyright date, that happens before. According to the memory map, address $7000 is the ECS EXEC ROM. Whatever it is, the test results in zero, causing it to fall through and ultimately crash.

 

I don't know much about the underlying EXEC, so I'm afraid I can't help much.

 

-dZ.

 

dZ,

 

The EXEC will check for "viable ROM" at $4800 and $7000 before doing most of its initialization, and if it see ROM at either location, it will jump to it.

 

My cart.mac will detect ROM segment overflows, but only if you put "ROMEND" at the end of your program. If you don't put ROMEND at the end of your program, you could successfully assemble your program, but you may have overflowed the last segment you assembled into. That's what I think happened here: The game edged out of $5000 - $6FFF into $7000, and the pre-title-screen code sees "viable ROM" at $7000 and jumps into what's actually data.

 

 

In other news, I wrote a quick and dirty Perl script to compress the Colossal Cave Phrase Data from the OS/8 version of Colossal Cave. The entire phrase database fits in about 7K words, using the extremely simplistic scheme I mentioned this morning. I'm attaching my scripts and their output.

 

--Joe

 

the message from jzintv when try to run the rom is: Halt! PC=7007 Instr= 82 MS= 62

Link to comment
Share on other sites

the message from jzintv when try to run the rom is: Halt! PC=7007 Instr= 82 MS= 62

 

Can you look at the listing file or maybe the .cfg file and see if your ROM edged into the $7xxx space? I suspect it did.

 

dZ tells me that there's an explicit "ROMEND" at the end of P-Mach, but if any of your code appears after ROMEND, then cart.mac can't catch ROM overflow.

Link to comment
Share on other sites

the message from jzintv when try to run the rom is: Halt! PC=7007 Instr= 82 MS= 62

 

Can you look at the listing file or maybe the .cfg file and see if your ROM edged into the $7xxx space? I suspect it did.

 

dZ tells me that there's an explicit "ROMEND" at the end of P-Mach, but if any of your code appears after ROMEND, then cart.mac can't catch ROM overflow.

 

I had Includes after ROMEND

I moved before like

 

 

;INSERITI INCLUDE PER IL TRACKER

INCLUDE "tracker.asm"

INCLUDE "macro/tracker.mac"

INCLUDE "demosong.asm"

;INSERITO INCLUDE PER IL FONT

INCLUDE "memunpk.asm"

 

ROMEND

INCLUDE "macro/stats.mac"

 

now I get error message segment overflow in segment 0

at least we know it is an overflow: what can I do now?

Edited by vprette
Link to comment
Share on other sites

That would do it. Segment overflow means you need to use ROMSEG to move some of your game out of the $5000 - $6FFF segment of address space into one of the other segments. I saw in another post that you're currently using the 16K address map. Your other two ROM segments are "ROMSEG 1" and "ROMSEG 2", which correspond to $Dxxx and $Fxxx.

 

If you think you're going to end up with a fairly large game (which, depending on what happens with the text, or if you add any graphics, you may very well), you might want to switch to the 42K map. You'll still need a ROMSEG to move some of your stuff over to a different segment, but ROMSEG 2 is huge all by itself (about 16K, $C022 - $FFFF), and ROMSEG 1 is about 12K.

 

I didn't create the ROMSEGs to make life difficult. The Intellivision has lots of fun potholes in its address map. Whee. "cart.mac" does what it can to paper over them, but it can't hide them completely.

Edited by intvnut
Link to comment
Share on other sites

the message from jzintv when try to run the rom is: Halt! PC=7007 Instr= 82 MS= 62

 

Can you look at the listing file or maybe the .cfg file and see if your ROM edged into the $7xxx space? I suspect it did.

 

dZ tells me that there's an explicit "ROMEND" at the end of P-Mach, but if any of your code appears after ROMEND, then cart.mac can't catch ROM overflow.

 

I had Includes after ROMEND

I moved before like

 

 

;INSERITI INCLUDE PER IL TRACKER

INCLUDE "tracker.asm"

INCLUDE "macro/tracker.mac"

INCLUDE "demosong.asm"

;INSERITO INCLUDE PER IL FONT

INCLUDE "memunpk.asm"

 

ROMEND

INCLUDE "macro/stats.mac"

 

now I get error message segment overflow in segment 0

at least we know it is an overflow: what can I do now?

 

Great! That's what we were expecting. Now we now how to fix it: just use more than one segment. "Cart.mac" makes this brain-dead simple with the macro "ROMSEG x":

 

Notice at the top of P-Machinery where it says "ROMSEG 0". Everything included after that will be assembled into segment #0. You need to find an appropriate place and switch segments like this:

 

      INCLUDE "blah1.asm"

      INCLUDE "blah2.asm"

      INCLUDE "blah3.asm"

      ROMSEG 1

      INCLUDE "blah4.asm"

      INCLUDE "blah5.asm"

 

You continue using different segments as they fill up.

 

-dZ.

Link to comment
Share on other sites

That would do it. Segment overflow means you need to use ROMSEG to move some of your game out of the $5000 - $6FFF segment of address space into one of the other segments. I saw in another post that you're currently using the 16K address map. Your other two ROM segments are "ROMSEG 1" and "ROMSEG 2", which correspond to $Dxxx and $Fxxx.

 

If you think you're going to end up with a fairly large game (which, depending on what happens with the text, or if you add any graphics, you may very well), you might want to switch to the 42K map. You'll still need a ROMSEG to move some of your stuff over to a different segment, but ROMSEG 2 is huge all by itself (about 16K, $C022 - $FFFF), and ROMSEG 1 is about 12K.

 

I didn't create the ROMSEGs to make life difficult. The Intellivision has lots of fun potholes in its address map. Whee. "cart.mac" does what it can to paper over them, but it can't hide them completely.

 

I think it's a safe bet by now that you are going to need the 42K map due to the size of the text data. This is also very simple with "cart.mac". In the main source file of your P-Machinery game, find the following line:

	 ROMSETUP 16K, 2010, "P-MACH Test", BOOT_UP, STACK_SIZE

 

and change it to,


	 ROMSETUP 42K, 2010, "P-MACH Test", BOOT_UP, STACK_SIZE

 

That's it, instant 42K memory map. The available segments and your usage should be returned in the output of the assembler by P-Machinery, something like this:


   Available:
     Segment 0  (8K): 841 words.
     Segment 1  (8K): 4401 words.
     Segment 2 (15K): 2322 words.
     Segment 3  (4K): 4096 words.
     Segment 4  (3K): 3840 words.
     Segment 5  (2K): 2034 words.
               TOTAL: 17534 words.

 

Notice that each one has a different size. You can use them in any order as long as you don't overflow them.

 

-dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

Notice that each one has a different size. You can use them in any order as long as you don't overflow them.

 

Not only that, but you can also switch ROMSEGs as often as you like, if you really need to pack things carefully. Example:

 

   ROMSEG 2
   INCLUDE "blah_a.asm"
   ROMSEG 1
   INCLUDE "blah_b.asm"
   ROMSEG 2
   INCLUDE "blah_c.asm"
   ROMSEG 1
   INCLUDE "blah_d.asm"
   ROMSEG 2
   INCLUDE "blah_e.asm"
   ROMSEG 1
   INCLUDE "blah_f.asm"
   ROMSEG 2
   INCLUDE "blah_g.asm"
   ROMSEG 1
   INCLUDE "blah_h.asm"

 

That is perhaps an extreme example. But, there are times when file "A" needs to be included before file "B", but you need them in different ROMSEGs. cart.mac is here to help.

 

As you can all tell, I'm a "cart.mac" cheerleader. :)

 

I guess I must have done an OK job, then. :-)

Link to comment
Share on other sites

Done some initial study on the walkthrough logic, and update status

 

New status (Blue is done, Green is faisable, Yellow is difficult. Red is very hard to code for me)

 

- TITLE SCREEN: ok (custom screen)

- INTRO: ok (welcome message)

- INIT: ok (integrate tracker and start demo music)

- FONT: ok (custom)

- CONTENTS: insert all levels message text descriptions - 38 over 214 [huge task]

- MUSIC: translate mod to decles of custom music "shadows" 0%

- PLAYER STATE: ok (add state PAGE)

- PLAY ENGINE1: ok (implement transition from PAGE to PAGE and PAGE to PLAY by reconnaising disc pressure)

- PLAY ENGINE2: ok (inputs from keypad reconnaise 8 directions, "Clear" as music pause and disc touch)

- PLAY ENGINE3: implement text input from keyboard 0%

- PLAY ENGINE4: implement level logic transition machine (game walkthrough) [huge task] - 14 macro_levels 0%

- PLAY ENGINE5: implement the parser to reconnaise text/keypad commands inserted to be used by login transition machine [huge task] 0%

- PLAY ENGINE6: ok (implement music pause)

- SCORE: implement scoring 0%

- BUG TESTING: 1 bug discovered/solved :-)

- BOX: 80%

- OVERLAYS: 0%

- MANUAL: 5%

Overall: 25%

Edited by vprette
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...