6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 3:21 am

All times are UTC




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Fri Mar 31, 2023 12:05 pm 
Offline
User avatar

Joined: Sun Dec 27, 2020 11:12 pm
Posts: 94
Location: France
Hi,

I recently got a CompactFlash card working on my Planck computer, and for some reason, I've been trying to create the smallest possible bootstrap code, that will copy code and data from the CF card into RAM and jump to it.

I'm down to 81 bytes (excluding reset vectors) and I thought it would be fun / interesting to see how this community would make it even smaller. Here is the code in question:

Code:
reset:
cf_init:
    jsr cf_wait
    lda #$E0
    sta CF_ADDRESS + 6
    lda #$1
    sta CF_ADDRESS + 1
    inc
    sta io_buffer_ptr + 1   ; store 2 in high byte of pointer
    stz io_buffer_ptr       ; store 0 in low byte of pointer
    lda #$EF
    sta CF_ADDRESS + 7
    jsr cf_wait
set_lba:
    ; start load at sector 0
    stz CF_ADDRESS + 3
    stz CF_ADDRESS + 4
    stz CF_ADDRESS + 5
    lda #$E0
    sta CF_ADDRESS + 6
read_sectors:
    ; Load enough sectors to fill the RAM
    lda #(((RAM_END-CODE_START)/$200)-1)
    sta CF_ADDRESS + 2
    lda #CF_READ_SECTOR_COMMAND
    sta CF_ADDRESS + 7
cf_read:
    lda CF_ADDRESS + 7 
    bmi cf_read         ; wait if not ready
    and #$08
    beq start           ; nothing left to read
    lda CF_ADDRESS
    sta (io_buffer_ptr)
    inc io_buffer_ptr
    bne cf_read
    inc io_buffer_ptr + 1
    bra cf_read
start:
    jmp (CODE_START)    ; The first two bytes of CF data contain an address to jump to

cf_wait:
    lda CF_ADDRESS + 7
    bmi cf_wait             ; wait FOR RDY to become unset
    rts


and here is a hexdump:
Code:
00000000  20 4b e0 a9 e0 8d d6 ff  a9 01 8d d1 ff 1a 85 01  | K..............|
00000010  64 00 a9 ef 8d d7 ff 20  4b e0 9c d3 ff 9c d4 ff  |d...... K.......|
00000020  9c d5 ff a9 e0 8d d6 ff  a9 3e 8d d2 ff a9 20 8d  |.........>.... .|
00000030  d7 ff ad d7 ff 30 fb 29  08 f0 0d ad d0 ff 92 00  |.....0.)........|
00000040  e6 00 d0 ee e6 01 80 ea  6c 00 02 ad d7 ff 30 fb  |........l.....0.|
00000050  60 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |`...............|


Have fun!

_________________
Jonathan Foucher

Take a look at the Planck 6502 computer.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 31, 2023 1:28 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
hmm, 2 things that come to my mind:
moving your IO (or atleast only the CF Card IO) into ZP so that all instructions accessing it are 1 byte smaller and you get to be able to use the 65C02's bit instructions.
making use of the index registers. they can be useful to keep constants that you might need multiple times in the code.

for example at the start you load $E0 into A, after that it gets overwritten and later you load $E0 again. so why not load $E0 into X for (and use STX instead of STA), so you don't need the second immediate load as X will keep the value.

on a side note, i've noticed that at the start of cf_read you do the same thing your cf_wait function does, so you could just call the cf_wait function and save 2 bytes.

then again, if you are able to get rid of all cf_wait calls (and the function itself) it saves a lot more bytes. doing that, plus moving IO to ZP and using some 65C02 bit instructions shrinks it down to 60 Bytes, including vectors!
and that actually small enough to allow you to re-insert 1 "cf_wait" function using the 65C02's BBS7 Instruction, i've put in 2 in the places where you originally had the JSR's, but only one can be uncommented, unless you can somehow free up 2 addtional bytes.

Code:
reset:
cf_init:
    ;BBS7 CF_ADDRESS + 7, cf_init   ; Wait if not ready
    LDX #0xE0
    STX CF_ADDRESS + 6
    LDA #1
    STA CF_ADDRESS + 1
    INC A
    STA io_buffer_ptr + 1           ; Store 2 in high byte of pointer
    STZ io_buffer_ptr               ; Store 0 in low byte of pointer
    LDA #0xEF
    STA CF_ADDRESS + 7
set_lba:
    ;BBS7 CF_ADDRESS + 7, set_lba   ; Wait if not ready
    ; start load at sector 0
    STZ CF_ADDRESS + 3
    STZ CF_ADDRESS + 4
    STZ CF_ADDRESS + 5
    STX CF_ADDRESS + 6
read_sectors:
    ; Load enough sectors to fill the RAM
    LDA #(((RAM_END - CODE_START) / 0x200) - 1)
    STA CF_ADDRESS + 2
    LDA #CF_READ_SECTOR_COMMAND
    STA CF_ADDRESS + 7
cf_read:
    BBS7 CF_ADDRESS + 7, cf_read    ; Wait if not ready
    BBR3 CF_ADDRESS + 7, start      ; Nothing left to read
    LDA CF_ADDRESS
    STA (io_buffer_ptr)
    INC io_buffer_ptr
    BNE cf_read
    INC io_buffer_ptr + 1
    BRA cf_read
start:
    JMP (CODE_START)        ; The first two bytes of CF data contain an address to jump to

of course if you cannot move your CF Card IO to ZP (or any reason) then my entire idea just falls apart. in that case, sorry i couldn't help much!


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 31, 2023 1:58 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1385
Just moving my post to the new thread as well....

Well, as I like say, you can't argue with success ;-)

A bit of feedback:

- As you already set the drive_head register for drive 0 and the upper 4 LBA address bits as zero, you shouldn't have to set them again (my assumption below)
- As this is pointed to by the reset vector, the stack pointer is not set by default (perhaps it doesn't matter)
- You can save a byte using a loop to fill the rest of the LBA number in to the 3 registers

Here's what I copy/pasted from my BIOS, with some changes, that should load a block to memory. It's a bit different, is 76 bytes, but untested!

Code:
;
;Reset Vector points to here!
; Assume you have a working IDE controller ;-)
; Just set it up to load LBA zero to memory and jump to it.
; - this assumes the single 512-byte contains rest of the boot loader, which can be more flexible
;
; NOTE: constants and variable are not shown here!
; - you need to define the IDE registers, the BIOS_XFER pointer in page zero and the load address!
;
;Init the IDE controller
; First, set the stack pointer, then setup the IDE controller to read a block into memory
;
IDE_INIT
                LDX     #$FF            ;Get $FF
                TXS                     ;Set Stack pointer
;
                JSR     TST_IDE_BUSY    ;Check/wait if IDE is still busy after HW Reset (6)
;
; Setup memory pointers first:
                LDA     #<LOAD_ADDR     ;Get Load address
                LDY     #>LOAD_ADDR     ; A/Y = Low/High
                STA     BIOS_XFERL      ;Set Page Zero pointer
                STY     BIOS_XFERH      ; Low/high
;
;IDE Setup
; First, set 8-bit mode, then setup for LBA zero to start loading from.
; - also set for a single block transfer (a partition record)
; - then send the read block command
;
                LDX     #$03            ;Set Index count for 3 (2)
PARM_XFER_LP
                STZ     IDE_SCT_NUM-1,X ;Zero IDE lower LBA registers (5)
                DEX                     ;Decrement count (2)
                BNE     PARM_XFER_LP    ;Loop back till done (2/3)
;
                INX                     ;Increment X to $01
                STX     IDE_SCT_CNT     ;Send to IDE for 1 block xfer (4)
;
                STX     IDE_FEATURE     ;Send to IDE controller (4)
                LDA     #%11100000      ;Get Drive 0, LBA. mode, etc. (2)
                STA     IDE_DRV_HEAD    ;Send to IDE controller (4)
                LDA     #$EF            ;Get Set Features Command (2)
                JSR     SEND_IDE_CMD    ;Send to IDE and wait till accepted
;
                LDA     #$20            ;Get LBA Read command
                JSR     SEND_IDE_CMD    ;Send to IDE and wait till accepted
;
; - Check for Data Request (DRQ), as the Read LBA operation is the main function
;   of the ISR, which will handle the data transfer from the IDE controller to store the
;   data into memory. This ISR will handle single and multiple block transfers.
;
LBA_XFER        LDA     IDE_STATUS      ;Get Status (clears IRQ) (4)
                AND     #%00001000      ;Check for DRQ (2)
                BEQ     IDE_RD_DONE     ;If not active, done, exit (2/3)
;
IDE_RD_RBLK
                LDA     IDE_DATA        ;Read a byte from IDE (4)
                STA     (BIOS_XFERL)    ;Store low byte (5)
                INC     BIOS_XFERL      ;Increment pointers (5)
                BNE     IDE_RD_RBLK     ; (2/3)
                INC     BIOS_XFERH      ; (5)
                BRA     LBA_XFER        ;Loop back until done
;
IDE_RD_DONE
                JMP     (LOAD_ADDR)     ;Jump to code to start
;
SEND_IDE_CMD
                STA     IDE_COMMAND     ;Send command to IDE
;
TST_IDE_BUSY
;
;Test for IDE Controller Busy
; This routine loops on the Busy flag. If the IDE Controller is busy, no other
; status register flags are valid and no commands can be sent to the IDE Controller.
; Hence, this routine is key to determine if the IDE Controller is available to
; accept a command. Bit 7 is the Busy Bit. The 65C02 will set the "n" flag
; if Bit 7 is active, else clear it.
;
                LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                BMI     TST_IDE_BUSY    ;Loop until BUSY bit is clear (2/3)
                RTS                     ;Return to Caller (6)
;


If you ditch the stack setup, that saves 3 bytes, if you just zero the one page zero pointer, that saves 2 more bytes. Also, you would add 2 bytes by loading more than 1 block.

Hope this helps...

I'm sure Bill (Plasmo) will share his boot code as well... even smaller.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 01, 2023 1:52 am 
Offline

Joined: Fri Dec 21, 2018 1:05 am
Posts: 1117
Location: Albuquerque NM USA
This is the bootstrap ROM in CRC65 that's 64 bytes. It is a dual-boot program that can either boot from master boot record of the CF disk or if it received a serial input during the nominal 1/2 delay between CF reset negation to CF busy negate then it will wait for 256 bytes of serial data and execute the 256-byte program. The program is 59 bytes long but I need 6 bytes to do IRQ, RESET, NMI vectors, so I "cheated" by reusing the last byte of last instruction "JMP $b000" as part of the NMI vector.

I took advantage of CF specification which requires sector count reg to be loaded with 1 and track/sector registers to be loaded with all zeros immediately after negation of reset. So it is always points at the master boot record after reset. All I need to do is wait for CF Busy flag to negate then issue a CF read command (0x20) and then wait for DRQ to set before reading CF data.

It is possible to build a state machine waiting for CF Busy negate, write 0x20, and wait for DRQ to set and then execute the data streaming out of CF disk; a truly ROM-less system. I did that with Z280RC.
Bill

=====================================================
Code:
000000r 1               ;1/7/20
000000r 1               ;ROM in CPLD of CRC65
000000r 1               ; two sections of code:
000000r 1               ;   serial bootstrap is 16 bytes, strapped to high ROM
000000r 1               ;   CF bootstrap is 30 bytes, strapped to low ROM
000000r 1               ; CF is in native 16 bit mode
000000r 1               ; only the low byte contains meaningful program
000000r 1               ; read data from master boot sector
000000r 1               ;   execute the program in master boot sector to load more program
000000r 1               
000000r 1               ;load 256-byte binary file to 0xB000
000000r 1               SerData = $f0f9      ;CPLD serial register
000000r 1               SerStat = $f0f8      ;CPLD serial status, bit 0 is receive ready, bit 1 is txempty
000000r 1               CFdata   = $ee00       ;CF data register
000000r 1               CFerr   = $ee01       ;CF error reg
000000r 1               CFsectcnt   = $ee02       ;CF sector count reg
000000r 1               CF07   = $ee03      ;CF LA0-7
000000r 1               CF815   = $ee04          ;CF LA8-15
000000r 1               CF1623   = $ee05          ;CF LA16-23
000000r 1               CF2427   = $ee06          ;CF LA24-27
000000r 1               CFstat   = $ee07          ;CF status/command reg
000000r 1               
000000r 1                  .ORG $ff80
00FF80  1               readbsy:
00FF80  1  AD F8 F0        LDA SerStat   ;check receive ready
00FF83  1  29 01           AND #1      ;receive ready is bit 0
00FF85  1  D0 1F           BNE serboot   ;if serial data ready, do serial bootstrap
00FF87  1  AA              TAX      ;initialize X to zero
00FF88  1  AD 07 EE        LDA CFstat   ;read CF status
00FF8B  1  2A              ROL      ;busy bit is bit 7
00FF8C  1  B0 F2           BCS readbsy   ;loop until either CF ready or received serial data
00FF8E  1  A9 20           LDA #$20   ;issue read CF command
00FF90  1  8D 07 EE        STA CFstat
00FF93  1               chkdrq:
00FF93  1  AD 07 EE        LDA CFstat   ;check data request bit set before read CF data
00FF96  1  29 08           AND #8      ;bit 3 is DRQ, wait for it to set
00FF98  1  F0 F9           BEQ chkdrq
00FF9A  1               getCFdata:
00FF9A  1  AD 00 EE        LDA CFdata
00FF9D  1  9D 00 B0        STA $b000,x   ;get 256 bytes of data to $b000
00FFA0  1  E8              INX
00FFA1  1  D0 F7           BNE getCFdata
00FFA3  1  4C 00 B0        JMP $b000
00FFA6  1               serboot:
00FFA6  1  AD F9 F0        LDA SerData   ;discard the first serial data
00FFA9  1               serboot1:
00FFA9  1  AD F8 F0        LDA SerStat
00FFAC  1  6A              ROR      ;receive ready is bit 0
00FFAD  1  90 FA           BCC serboot1   ;if serial data ready, do serial bootstrap
00FFAF  1  AD F9 F0        LDA SerData
00FFB2  1  9D 00 B0        STA $b000,x
00FFB5  1  E8              INX
00FFB6  1  D0 F1           BNE serboot1
00FFB8  1  4C 00 B0        JMP $b000


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 02, 2023 11:23 am 
Offline
User avatar

Joined: Sun Dec 27, 2020 11:12 pm
Posts: 94
Location: France
Very nice Bill, I will be studying your code closely. I actually tried not setting the track/sector registers, as that saves around 9 bytes, but then I could not read from the card. I'll have to try again, because maybe something else was wrong while I was testing this.

_________________
Jonathan Foucher

Take a look at the Planck 6502 computer.


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 02, 2023 4:00 pm 
Offline

Joined: Sat Dec 12, 2015 7:48 pm
Posts: 145
Location: Lake Tahoe
Bill, in your above code:
Code:
00FF88  1  AD 07 EE        LDA CFstat   ;read CF status
00FF8B  1  2A              ROL      ;busy bit is bit 7
00FF8C  1  B0 F2           BCS readbsy   ;loop until either CF ready or received serial data

would
Code:
        BIT CFstat
        BMI readbsy

not do the same?


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 03, 2023 9:48 am 
Offline

Joined: Fri Dec 21, 2018 1:05 am
Posts: 1117
Location: Albuquerque NM USA
Yes! using BIT instruction would save 1 byte of opcode. Thank you.
Bill


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 03, 2023 10:28 am 
Offline

Joined: Wed Jun 23, 2021 8:02 am
Posts: 166
I think there's a problem with the serial download option in the code above. I can't see where X gets initialized if serial boot is in use - it looks like it's only set once it's decided to use CF card.
You could fix that without increasing code size as follows:

Code:
RESET_ENTRY:
    LDX     #0
    LDA     SerStat
    LSR
    BCS     SERIAL_BOOT
WAIT_CF_RDY:
    BIT     CFstat
    BMI     WAIT_CF_RDY
    ...


You could save one more byte by replacing the first JMP $B000 instruction (at $FFA3 in the original listing) with a BEQ (or BRA if you're using a 65C02 or later) to the second one.
You could save a couple more bytes if you changed your CPLD to output the serial 'receive data ready' status bit on D7 instead of D0 so you could test using the N flag instead of needing a rotate or AND instruction.


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 03, 2023 10:54 am 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1488
Location: Scotland
kernelthread wrote:
You could save a couple more bytes if you changed your CPLD to output the serial 'receive data ready' status bit on D7 instead of D0 so you could test using the N flag instead of needing a rotate or AND instruction.


I had a bit of a "Hmmm" moment earlier when I saw the BIT instruction as my own use of it has been the AND feature with the contents of Acc, however refreshing the old grey cells, I remembered it can also directly test bits 7 (negative) as well as bit 6 (overflow) without caring for the contents of the Acc...

So if you have programmable hardware you have bits 6 and 7 as signal inputs that'll let you save a byte here and there...

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 03, 2023 1:54 pm 
Offline
User avatar

Joined: Sun Dec 27, 2020 11:12 pm
Posts: 94
Location: France
plasmo wrote:
Yes! using BIT instruction would save 1 byte of opcode. Thank you.
Bill

Actually I don't think you'd even need the BIT instruction, would you? LDA sets the negative flag so you could just do

Code:
LDA CFstat
BMI readbsy


Not that it changes anything compared to using BIT...

_________________
Jonathan Foucher

Take a look at the Planck 6502 computer.


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 03, 2023 3:33 pm 
Offline

Joined: Fri Dec 21, 2018 1:05 am
Posts: 1117
Location: Albuquerque NM USA
kernelthread wrote:
I think there's a problem with the serial download option in the code above. I can't see where X gets initialized if serial boot is in use - it looks like it's only set once it's decided to use CF card.
You could fix that without increasing code size as follows:

Code:
RESET_ENTRY:
    LDX     #0
    LDA     SerStat
    LSR
    BCS     SERIAL_BOOT
WAIT_CF_RDY:
    BIT     CFstat
    BMI     WAIT_CF_RDY
    ...


You could save one more byte by replacing the first JMP $B000 instruction (at $FFA3 in the original listing) with a BEQ (or BRA if you're using a 65C02 or later) to the second one.
You could save a couple more bytes if you changed your CPLD to output the serial 'receive data ready' status bit on D7 instead of D0 so you could test using the N flag instead of needing a rotate or AND instruction.


The reason regX in the original code is set to 0 is because serial port status is driven by user input so never ready immediately after reset, so the "BNE serboot" will not branch to serboot initially and next instruction "TAX" will be executed to clear regX.

I don't think branch instructions will work because the target (0xb000) is too far.

BIT instruction is cool for putting status at D6 and D7, I need to remember that. The same serial port hardware is also designed for Z80 and 680x0, so I'm balancing consistent hardware against smallest code. Smallest code is more important when I'm running out of memory, but I'm good for now.
Bill


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 03, 2023 6:44 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
jfoucher wrote:
Actually I don't think you'd even need the BIT instruction, would you? LDA sets the negative flag so you could just do

Code:
LDA CFstat
BMI readbsy

Not that it changes anything compared to using BIT...

BIT also reads the byte, so the LDA is not needed, which is why resman's example leaves out the LDA.  BIT lets you keep the contents of the accumulator undisturbed.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 03, 2023 6:45 pm 
Offline

Joined: Wed Jun 23, 2021 8:02 am
Posts: 166
plasmo wrote:
I don't think branch instructions will work because the target (0xb000) is too far.

I was thinking of this:
Code:
    ; CF card boot code
    ...
    BRA     FINISH

    ; Serial boot code
    ...
FINISH:
    JMP     $B000

But it only saves 1 byte so not a big deal.


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 03, 2023 7:41 pm 
Offline

Joined: Fri Dec 21, 2018 1:05 am
Posts: 1117
Location: Albuquerque NM USA
kernelthread wrote:
I was thinking of this:
Code:
    ; CF card boot code
    ...
    BRA     FINISH

    ; Serial boot code
    ...
FINISH:
    JMP     $B000

But it only saves 1 byte so not a big deal.

Oh, yes. That’s a good idea. Every byte count when I only have 64 bytes to work with. With all these great suggestions may be it is possible to boot from CF in 32 bytes after all.
Bill


Top
 Profile  
Reply with quote  
PostPosted: Sun Sep 24, 2023 11:37 am 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
Here's a 42-byte suggestion, it might require too many changes to CPLD behaviour though and I made an assumption that might not be valid about the CFstat register.

I noted some interface bit changes at the top of the code. There is an assumption that, when polling CFstat for DRQ, the "busy" bit will be clear, not set. If that's wrong then an extra "CLV" seems to be needed, however there is also another solution perhaps that would avoid the need for that as well; and other CPLD changes could also fix this, e.g. inverting the sense of the "busy" bit.

I shied away from suggesting swapping bits when writing CFstat so that the command sent here is #$01 instead of #$20 - it might save another byte or two, but is arguably too intrusive a change.

I would also say that it strikes me that you could just not support serial here, always boot from CF, but have the CF bootloader immediately test whether it should reboot from serial after it's loaded. It would still allow rapid iteration in development via serial, but would require a CF card to be present with a working bootloader.

Moving the I/O into zero page would save 7 more bytes by my count and also allow use of some additional operations (BBR, BBS).

Edit - is it OK to write garbage to SerStat? If so then you don't need to change the bits there, we could LSR SerStat and use the C flag to indicate whether we're loading from CF or Serial, instead of the V flag.

Code:
; Interface Changes
;    SerStat receive ready bit is on bit 6 instead of 0
;    CFstat busy is on bit 6 instead of 7
;    CFstat DRQ is on bit 7 instead of 3 (could also use bit 5)
;    Don't discard one byte of serial data, just store it anyway
;    Relies on CF busy flag being not set when polling for DRQ
;    Boot code is loaded into zero page and branched to

    .ORG $ff80

    LDX #0        ;initialize X to zero
readbsy:
    BIT SerStat   ;check receive ready (bit 6)
    BVS bootloop  ;if serial data ready, boot from serial

    BIT CFstat    ;read CF status; busy bit is bit 6
    BVS readbsy   ;loop until either CF ready or received serial data

    LDA #$20      ;issue read CF command
    STA CFstat
chkdrq:
    BIT CFstat    ;check data request bit set before read CF data
    BPL chkdrq    ;bit 7 is DRQ, wait for it to set
    ;CLV          ;bit 6 is busy, will be clear now anyway?

bootloop:
    LDA CFdata
    BVC store     ; overflow flag is set for serial, clear for CF
serwait:
    BIT SerStat
    BVC serwait   ; wait for receive ready (bit 6)
    LDA SerData
store:
    STA $0,x
    INX
    BNE bootloop
    BRA $0


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC


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:  
cron