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

All times are UTC




Post new topic Reply to topic  [ 66 posts ]  Go to page 1, 2, 3, 4, 5  Next
Author Message
PostPosted: Thu Jun 18, 2020 4:22 am 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
Hello everyone

40 years ago I dreamed about building my own computer. But then buying them was so much easier. Then I saw Ben Eaters video and got really hooked. My first attempt derived from Ben's SAP1 was a success (or as my father put it "a lot of effort to calculate 1+1"). But I had to realize that expanding that system beyond the stage of "demonstration level speeds" would not really work, so I'm going for a 6502 based design as Mark2.

After about a month of research I had to realize that the biggest obstacle would be video. For modern monitors it has to be at least VGA, and I have not found any simple video chip that could be used. I don't want to use microcontrollers or FPGA. That left a solution like

https://tldr.fi/2014/09/27/zc160-vga-adapter1/
https://www.bigmessowires.com/2008/07/2 ... em-design/
or
viewtopic.php?f=4&t=3329

I especially liked the Vulcan design, although IMHO from those projects listed it's the one least documented. Unfortunately it seems to be dead in the water :( Still my design borrows heavily from Vulcan, so many thanks Brad! :D

Design goals:
VGA Video 640x480 or 800x600
no FPGA or micro crontroller
I like the Vulcan copy machine
16k part of the video buffer should be mapped to the CPU 64kb memory space
some extra memory for data (64kb is not much for data)
I also want to add a AMD coprocessor ... just for the kicks.
64 colors (2 bit per color)

I came up with this layout:
Attachment:
sketch1_v01.jpg
sketch1_v01.jpg [ 141.39 KiB | Viewed 3873 times ]

The CPU controls a number of 574 chips. I will need a number of GALs to create the selection logic. The whole address space looks like

0000-00FF Page Zero
0100-01FF Stack
0200-03FF I/O space
0400-7FFF memory
8000-BFFF 16kb window
C000-FFFF 16kb ROM
Attachment:
main_computer.jpg
main_computer.jpg [ 70.69 KiB | Viewed 3873 times ]

The extra 574 register to the top right helps to map the extra memory (16kb at a time).

The Video memory is double buffered. One buffer is used by the sync counters to generate the VGA signal. The second buffer is written to in parallel either from CPU or the copy machine. That means I have a number of possible datapaths between the components, and I plan to use (a lot) 245 chips to separate each of them. Another issue is the bus width of 9 and 10 bits - that adds the need of some extra 245 chips.
Attachment:
video buffer and DAC.jpg
video buffer and DAC.jpg [ 101.05 KiB | Viewed 3873 times ]

The sync signal is generated by 2 counters and a 512kb memory chip. I will need to set up correct information in the memory during initialization of the computer. The 8bits will also reset the 2 sync counters at the right time.
Attachment:
sync_counter.jpg
sync_counter.jpg [ 39.67 KiB | Viewed 3873 times ]

There is a copy machine. The source counter is made up of 193 chips and can be switched between 2 separate 12bit counters and a full 24bit counter. I figure a simple transistor between counter four and five should do the job, pulling the ripple input low if I need 2 separate counters.
Attachment:
source counter.jpg
source counter.jpg [ 72.31 KiB | Viewed 3873 times ]

The destination counter is made up by counter-comparator-register trios forming 2 10/9 bit counters to address the buffer ram.
Attachment:
target counter.jpg
target counter.jpg [ 42.2 KiB | Viewed 3873 times ]

This little trick should enable me to access a 64x64 (7bit by 7bit) Window of the Video memory. Adding 3x bits and 2y bits to the IPC of the 6502 should allow me to directly draw into the video buffer
Attachment:
64x64 mapping.jpg
64x64 mapping.jpg [ 24.59 KiB | Viewed 3873 times ]

So please tell me what you think and help me straighten up the design
thanks
Michael

_________________
No simulation survives contact with reality!


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 18, 2020 8:35 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
Welcome Michael!

Nothing if not ambitious - anything is possible, but some things take longer to build or longer to debug. I'd recommend an incremental approach, especially for any aspect you haven't tackled before.

Also, while anything is possible, as you add complexity you are likely to be sacrificing clock speed, so if you want an unlimited complexity budget, you might not meet some specific clock speed that you had in mind. And the closer your design gets to not quite working at speed, the harder the debug. So a modest speed goal might be a good place to start.


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 18, 2020 2:30 pm 
Offline

Joined: Thu Jan 16, 2020 3:52 pm
Posts: 45
very interesting design. for practical approach I personally think using a FPGA/CPLD is test out the design (I know you said you didn't want to use FPGA); otherwise it will take alot of time and effort to wire-up all the TTL chips and debug the design.


Top
 Profile  
Reply with quote  
PostPosted: Sat Jun 27, 2020 2:07 pm 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
How many chips can the WDC 6502 drive on the address bus without additional buffering? My current estimate is I need 5 GALs for the decoding logic alone.

thanks
Michael

_________________
No simulation survives contact with reality!


Top
 Profile  
Reply with quote  
PostPosted: Sat Jun 27, 2020 2:08 pm 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
maded2 wrote:
very interesting design. for practical approach I personally think using a FPGA/CPLD is test out the design (I know you said you didn't want to use FPGA); otherwise it will take alot of time and effort to wire-up all the TTL chips and debug the design.


that is my project for the next 1-2 years - time is not an issue :)

_________________
No simulation survives contact with reality!


Top
 Profile  
Reply with quote  
PostPosted: Sat Jun 27, 2020 4:34 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1488
Location: Scotland
It's a very ambitious project, but have you thought about the sort of speed the 6502 will be able to write to the video RAM? I suspect that 640x480 (at 8bits per pixel) will take far to long to clear the screen, let alone write some text to or draw a line, or scroll.

Even if you used the 65816 block-move instruction, you have 300KB of RAM to clear, so at 7 cycles per byte on a 1Mhz CPU it will take a shade over 2 seconds. It will be worse on the 6502 as it'll take more cycles.... Get the clock up to 14Mhz or more and it will obviously be much better but you're not going to get 30fps out of it...

Back in the late 70's/early 80's the consumer level display technology (ie. TV sets) mostly matched the hardware capabilities so it wasn't much an issue then - we put up with what we got - 280x192 on the Apple II taking 8KB of RAM, and a couple of years later the BBC Micro dedicated 20KB of it's 32KB RAM to the highest resolution (640x256x1bpp) and we wondered how we'd be able to write any code for it! But don't let that put you off... Especially if you can separate the video part well enough to adapt other CPUs to it in the future, should you want to - or even "FPGA" the whole video part for other retro-system to use?

Hope it goes well!

Cheers,

-Gordon

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Jun 27, 2020 5:27 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
ThePhysicist wrote:
How many chips can the WDC 6502 drive on the address bus without additional buffering? My current estimate is I need 5 GALs for the decoding logic alone.

From page 5 of the 6502 primer, on logic families and timing margins:

    You will not need bus transceivers with a CMOS 6502, especially WDC's current production W65C02S. I have done some brief tests on the W65C816S's pin drivers. Their behavior was pretty much symmetrical, able to pull up just as hard as they can pull down, unlike TTL which cannot pull up as hard. If you had to boil my test results down to approximations and treat the circuits as just a resistance, the data pin drivers acted very roughly like a SPDT switch with 50Ω in series with the common terminal (ie, the output); and the address bus pins, as a SPDT switch with 60Ω in series. I have not had the chance to test a W65C02S; but I suspect WDC used the same circuits on the '02 and the '816, which would make it much stronger than the data sheet says. The time constant of 60Ω times the capacitive load of 10 CMOS loads is around 3ns, which is less added delay than you'll get from a bus transceiver IC. Daryl Rictor had no trouble running my 4Mx8 5V 10ns SRAM module on his SBC-4 single-board computer at 12MHz with a barefoot '816 (ie, no bus transceivers), driving this module and three daughter boards at the same time. The module has 8 bussed SRAM ICs.

In the article on the differences between the NMOS and CMOS 6502's, I also added, regarding the 65c22 VIA, not the processor:

    In a separate test on WDC's W65C22S VIA (not the W65C22N) I/O pins years earlier, I found they were each able to pull to within 0.8V of either rail with a 220-ohm resistor to the opposite rail, meaning a 19mA load, even pulling up, and give 50mA into a dead short. Rockwell's R65C22 could pull down with 100mA into a dead short, but could not pull up as hard, not being symmetrical like WDC's.

_________________
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: Sat Jun 27, 2020 7:34 pm 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
GARTHWILSON wrote:
ThePhysicist wrote:
How many chips can the WDC 6502 drive on the address bus without additional buffering? My current estimate is I need 5 GALs for the decoding logic alone.

From page 5 of the 6502 primer, on logic families and timing margins:

    You will not need bus transceivers with a CMOS 6502, especially WDC's current production W65C02S. I have done some brief tests on the W65C816S's pin drivers. Their behavior was pretty much symmetrical, able to pull up just as hard as they can pull down, unlike TTL which cannot pull up as hard. If you had to boil my test results down to approximations and treat the circuits as just a resistance, the data pin drivers acted very roughly like a SPDT switch with 50Ω in series with the common terminal (ie, the output); and the address bus pins, as a SPDT switch with 60Ω in series. I have not had the chance to test a W65C02S; but I suspect WDC used the same circuits on the '02 and the '816, which would make it much stronger than the data sheet says. The time constant of 60Ω times the capacitive load of 10 CMOS loads is around 3ns, which is less added delay than you'll get from a bus transceiver IC. Daryl Rictor had no trouble running my 4Mx8 5V 10ns SRAM module on his SBC-4 single-board computer at 12MHz with a barefoot '816 (ie, no bus transceivers), driving this module and three daughter boards at the same time. The module has 8 bussed SRAM ICs.

In the article on the differences between the NMOS and CMOS 6502's, I also added, regarding the 65c22 VIA, not the processor:

    In a separate test on WDC's W65C22S VIA (not the W65C22N) I/O pins years earlier, I found they were each able to pull to within 0.8V of either rail with a 220-ohm resistor to the opposite rail, meaning a 19mA load, even pulling up, and give 50mA into a dead short. Rockwell's R65C22 could pull down with 100mA into a dead short, but could not pull up as hard, not being symmetrical like WDC's.


thanks!

_________________
No simulation survives contact with reality!


Top
 Profile  
Reply with quote  
PostPosted: Sat Jun 27, 2020 7:39 pm 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
drogon wrote:
It's a very ambitious project, but have you thought about the sort of speed the 6502 will be able to write to the video RAM? I suspect that 640x480 (at 8bits per pixel) will take far to long to clear the screen, let alone write some text to or draw a line, or scroll.

Even if you used the 65816 block-move instruction, you have 300KB of RAM to clear, so at 7 cycles per byte on a 1Mhz CPU it will take a shade over 2 seconds. It will be worse on the 6502 as it'll take more cycles.... Get the clock up to 14Mhz or more and it will obviously be much better but you're not going to get 30fps out of it...

-Gordon


the main copy/clear operations happen in the video system itself based of 74F chips at 10MHz+. There is an error I discovered after posting in the data bus routing - but the idea is the 6502 writes into the 4MB video ram to set up he graphics. What is necessary is then copied via the multiple counters to either of the buffer memory. Once a buffer memory has been set up, and a VGA cycle is complete, the 6502 switches the 2 buffers.

At least that's the theory ......

_________________
No simulation survives contact with reality!


Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 06, 2020 2:03 pm 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
So I've completed the first step, and wanted to report about my experiences :)

I started to layout a test board with enough connections to add the video systems step by step later.
Attachment:
cpu_board_layout_p2_v01_0200607.jpg
cpu_board_layout_p2_v01_0200607.jpg [ 566.51 KiB | Viewed 3491 times ]

Attachment:
cpu_board_layout_p1_v01_0200607.jpg
cpu_board_layout_p1_v01_0200607.jpg [ 886.12 KiB | Viewed 3491 times ]

Attachment:
cpu_board_v01_0200607.jpg
cpu_board_v01_0200607.jpg [ 192.44 KiB | Viewed 3491 times ]


PCBway delivered very quickly:
Attachment:
cpu_board_v1_20200706.jpg
cpu_board_v1_20200706.jpg [ 131.64 KiB | Viewed 3491 times ]


and after 2 weeks of debugging v1 was running (printing to LCD display and communicating over serial line with PC at 500kHz)
Attachment:
mark01_v1_20200706.jpg
mark01_v1_20200706.jpg [ 117.68 KiB | Viewed 3491 times ]

The breadboard contains an external timing circuit to single step or up to 100Hz, as well as an LCD display (connected to VIA1) and the GAL organizing the memory layout. The board currently is only half populated, I could add one ROM, one RAM, one VIA and one ACIA chip in the future. The Arduino is only a cheap logic analyzer.

So what have I learned?

    bringing 2 chip select lines to the external control board is really stupid (duh, now fixed on backside with wires :P)
    bringing all RW signals out to control board is probably worse (double duh, now also fixed on backside with wires)
    the reach of a 6 month old is greater than you can imagine, and you will debug for 2 hours until you find the missing resistor
    there is an irresistible force drawing 3 year old boys to wires. He will "look" with his fingers, the wires will come out - so naturally the boy will put them back (out of order) for another 2 hours of debugging....
    those chips are pretty hardy - if you think you ruined one of them, think again and check your layout. After some debugging you'll still think the chip is broken .... so THINK AGAIN (and read this forum; thanks Garth for http://forum.6502.org/viewtopic.php?f=4&t=4346#p49340)
    there is a 99% chance the USB-serial cables you have at home are not compatible with TTL serial from a 6551 (though the cables to connect to an Arduino work nicely)
    no matter how simple a connection, if you have 2 options you will connect them in the wrong order and then go bug hunting in the wrong places
    a simple logic probe is a good friend
    using an Arduino to watch address and data lines brings a lot of insight what the CPU is doing

lots of fun :)

_________________
No simulation survives contact with reality!


Last edited by ThePhysicist on Mon Jul 06, 2020 4:31 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 06, 2020 2:16 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
ThePhysicist wrote:
the reach of a 6 month old is greater than you can imagine
[...]
there is an irresistible force drawing 3 year old boys to wires

Future forum members! :mrgreen:

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 18, 2020 12:17 am 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
So baby steps progress, but still progress!

Attachment:
cpu_board_v01_20200717.jpg
cpu_board_v01_20200717.jpg [ 571.22 KiB | Viewed 3371 times ]


I put LCD and GAL on little PCBs and it's still working as expected at 500kHz. At that point I decided I need to look at a software pipeline. I need at minimum a little monitor and the ability to peek and poke around on the address bus. Probably most people here will "yell" at me - for not using pure assembler :P

But while I have some 35 years experience in c-programming, my assembler is pretty thin - and because days might pass between some time developing code, I would quickly forget my plans and start again. So c it is, and I came down with 4 alternatives

a) x86-to-6502 translator by Lefticus (https://github.com/lefticus/x86-to-6502/) - I really like the idea, modern compiler optimizations are really good .... but it failed to translate my simple monitor program. Tried it with a dozen different compilers, but no luck. As Lefticus writes correctly, that program is more of a case study then a full developed translator.
b) WDC c-compiler - I had so high hopes for it, but again it failed the basic test. Crushed with a memory error on my simple code piece. So not an option either
c) CC65 - the first one to actually compile the program.
d) gcc 6502 port found here https://github.com/itszor/gcc-6502-bits - also passed the basic test. And produced smaller code (1100 vs 1600 bytes). So for the moment this will be my plan to use in my first iteration of the monitor.

_________________
No simulation survives contact with reality!


Last edited by ThePhysicist on Sat Jul 18, 2020 4:08 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 18, 2020 8:35 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
There's a gcc port found here too, formerly known as puppeh's but now itszor's:
https://github.com/itszor


Top
 Profile  
Reply with quote  
PostPosted: Sat Jul 18, 2020 4:16 pm 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
BigEd wrote:
There's a gcc port found here too, formerly known as puppeh's but now itszor's:
https://github.com/itszor


That's what I'm going to use - I fixed the URL in my post. You have to use https://github.com/itszor/gcc-6502-bits, not https://github.com/itszor/gcc-6502. The later is the port, but cannot be compiled directly. gcc-6502-bits compiled without issues on Cygwin with a gcc 7, and provides tinylibc as well.

One problem - it has support for BCC, c64 and the "integrated" semi65x simulator - but self build systems are a different topic altogether. It also uses the cc65 linker (ld65), which means I'm banging my head now against the ld65 .cfg file (for memory layout). Does anyone know a good howto on the topic? The original documentation is a bit ... cumbersome. I prefer explanations along simple examples :)

_________________
No simulation survives contact with reality!


Top
 Profile  
Reply with quote  
PostPosted: Tue Jul 28, 2020 1:40 pm 
Offline

Joined: Wed Jun 17, 2020 10:51 am
Posts: 60
Two steps back, one step forward ....

So I wrote a simple monitor program in C with a bit of assembler startup code. Naturally your first attempt does not produce anything, so for debugging I went looking for a simulator and was very happy to find https://github.com/sethm/symon, because it also simulates a 6551 chip! Then I run into a problem with understanding the assembler code that my toolchain (6502-gcc and ld65) produced. So I wrote a small python program that combines the annotated assembly produced by the gcc with the actual addresses created by the linker. That would result in something like
Code:
                                                      .export int4_to_hexchar
                                                   int4_to_hexchar:
                                                   ; frame size 0, pretend size 0, outgoing size 0
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:118:     if(a>9)
C0DD A9 09            lda     #$09                    lda #$09   ;# tmp98,
C0DF C5 04            cmp     $04                     cmp _r0      ;# tmp98, a
C0E1 A9 00            lda     #$00                    lda #$00   ;# tmp98,
C0E3 E5 05            sbc     $05                     sbc _r1      ;# a
C0E5 50 02            bvc     LC0E9                   bvc L@9      ;#
C0E7 49 80            eor     #$80                    eor #$80   ; negate top bit
                                                   L@9:
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:119:         return('A'-9 + a);
C0E9 08               php                             php
C0EA A5 04            lda     $04                     lda _r0      ;# <retval>, a
C0EC 28               plp                             plp
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:118:     if(a>9)
C0ED 10 06            bpl     LC0F5                   bpl L@7      ;#
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:119:         return('A'-9 + a);
C0EF 18               clc                             clc
C0F0 69 38            adc     #$38                    adc #$38   ;#
                                                   L@10:
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:121:         return('0' + a);
C0F2 85 04            sta     $04                     sta _r0      ;# <retval>, <retval>
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:122: }
C0F4 60               rts                             rts
                                                   L@7:
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:121:         return('0' + a);
C0F5 18               clc                             clc
C0F6 69 30            adc     #$30                    adc #$30   ;#
C0F8 4C F2 C0         jmp     LC0F2                   jmp L@10      ;#

for a simple c-code:
Code:
/* convert 4bit int to hex char */
const char int4_to_hexchar(int a)
{
    if(a>9)
        return('A'-9 + a);
    else
        return('0' + a);
}

Honestly, I had to swallow hard when I saw the overhead c created. Still I started to debug away, found a couple of minor errors in logic (for instance the compiler had optimized away "unnecessary" loops), until I hit a wall. Possibly I'm too stupid...but no matter what I did, the program simply did not echo back the characetrs I sent over serial. The c-code I thought was pretty straightforward:
Code:
const char acia_getc()
{
    asm("\nwait_for_receive:\n\tlda " STR_ACIA_STATUS ";\n\tand #" XSTR(ACIA_STATUS_RX_FULL)";\n\tbeq wait_for_receive;");

    return(ACIA_DATA[0]);
}

/* read a '\x0a' terminated string from ACIA1 */
void acia_gets()
{
    char c;
    *ACIA_INDEX=0;
    while(c=acia_getc() != '\x0d')
    {
        RES[0] = c;
        acia_putc(RES[0]);
.....

but the compiler gave me was
Code:
                                                   acia_getc:
                                                   wait_for_receive:
C238 AD 01 88         lda     $8801                   lda $8801;
C23B 29 08            and     #$08                    and #1 << 3;
C23D F0 F9            beq     LC238                   beq wait_for_receive;
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:179: }
C23F AD 00 88         lda     $8800                   lda $8800
C242 85 04            sta     $04                     sta _r0
C244 60               rts                             rts


C269 20 38 C2         jsr     LC238                   jsr acia_getc
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:186:     while(c=acia_getc() != '\x0d')
C26C A5 04            lda     $04                     lda _r0
C26E C9 0D            cmp     #$0D                    cmp #$0d
C270 D0 49            bne     LC2BB                   bne L@35
.....
                                                   L@35:
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:188:    RES[0] = c;
C2BB A9 01            lda     #$01                    lda #$01
C2BD 8D 00 03         sta     $0300                   sta $0300
                                                   ;# firmware_v01_monitor_and_lcd_cc65.c:189:         acia_putc(RES[0]);
C2C0 85 04            sta     $04                     sta _r0
C2C2 20 01 C2         jsr     LC201                   jsr acia_putc

So a character is received in $8800 (ACIA data port), stored in _r0 aka $0004, the subroutine returns, loads _r0 into the ACC, compares with $0d, jumps to L@35, then loads 1 into ACC and stores it into _r0 before echoing the character back? Do I understand that right??? (I think I do, I pondered over that for 2 hours....)

So I abandoned gcc at that point. I seemed easier to start learning 6502 assembly for real :)
3 days later I had a working monitor, that can display and modify memory locations. So back to the hardware now to layout my first parts of the VGA card!

_________________
No simulation survives contact with reality!


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

All times are UTC


Who is online

Users browsing this forum: Google [Bot] and 32 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: