Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
gonadic io
Feb 16, 2011

>>=
sometimes i want to do more hardware stuff then i realise that i'd have to janitor bits and change my mind

Adbot
ADBOT LOVES YOU

gonadic io
Feb 16, 2011

>>=

gonadic io posted:

is there a reason to buy an arduino zero over a MKRZero? The micro touts that is has the same pins, and it's about half the price. Is it just a more modern (smaller) upgrade?

Also it runs arm so it's fairly easy to get rust to compile on!

I bought a CO2 tank, a regulator, some solenoid valves, and a gyroscope chip. Here's hoping that I actually do something with it

gonadic io
Feb 16, 2011

>>=
I have a little gyroscope chip that I'd like to connect to a small pcb so I can mount it in the desired orientation. It doesn't have pins though, it looks like this:

It's roughly 1cm a side.

What's the usual way to connect one of these? I saw one person mount it upside down on the board, and solder wires to each of the pads. This is pretty fiddly though, is this the usual way to do it?

The device is 5V and draws 51mA. Is a 0.01microF bypass okay? I plan to put it on the board itself as I'll never not want it.

Sorry for the stupid questions but I am very new to the wonderful world of hardware.

gonadic io
Feb 16, 2011

>>=

Poopernickel posted:

decoupling is actually a super-complicated topic - more uFs (uF uF uF) aren't always better

remember: it's not the size of the capacitor, it's how you gently caress

I'd plop down a 0.1uF in the smallest package you're comfortable with, right next to each power pin. if you need ~precise~ measurements, maybe add a 1uF and an 0.01uF in parallel

i do need as ~precise~ as possible, since in theory this is one day supposed to be a rocket guidance system: https://github.com/djmcgill/to-the-moon

i will check the data sheet though.

the chip arrived today!! but my anti-static wriststrap hasn't though so i can't open the bag. i really am starting from nothing here.

gonadic io
Feb 16, 2011

>>=

hobbesmaster posted:

lol an anti static strap, look at this noob


Bloody posted:

ESD is a myth

brb putting on my woollen jumper, playing with my cats and some balloons, moonwalking across my carpet and then working with this £50 chip

gonadic io
Feb 16, 2011

>>=
my zero should be arriving today! i already wrote some (rust) code for it, based off the great hannobraun.de/embedded/

that's for a duo though, so none of the memory addresses are the same (which I looked up from the data sheet). hopefully it'll work.

gonadic io
Feb 16, 2011

>>=

JawnV6 posted:

good luck!

just curious, how nicely does rust play with gdb/lldb?

seems fine, i've not tried it: https://michaelwoerister.github.io/2015/03/27/rust-xxdb.html

gonadic io
Feb 16, 2011

>>=
im going insane here trying to get rust onto my arduino zero.

this c program, when uploaded via the arduino IDE, turns the led on:

code:
void setup() {
  *(unsigned long *)0x41004408 = 131072ul;   // Set the direction of the port pin PA17 to an output
  *(unsigned long *)0x41004418 = 131072ul;   // Switch the output to 1 or HIGH
}
i wrote the same rust code, which produces the following binary:
code:
$ arm-none-eabi-readelf -h target/arduino-zero/debug/zero
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x2001
  Start of program headers:          52 (bytes into file)
  Start of section headers:          12528 (bytes into file)
  Flags:                             0x5000200, Version5 EABI, soft-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         3
  Size of section headers:           40 (bytes)
  Number of section headers:         18
  Section header string table index: 17
code:
$ arm-none-eabi-objdump -Cd target/arduino-zero/debug/zero

target/arduino-zero/debug/zero:     file format elf32-littlearm


Disassembly of section .text:

00002000 <Reset_Handler>:
    2000:	b081      	sub	sp, #4
    2002:	e7ff      	b.n	2004 <Reset_Handler+0x4>
    2004:	e7ff      	b.n	2006 <Reset_Handler+0x6>
    2006:	f244 4008 	movw	r0, #17416	; 0x4408
    200a:	f2c4 1000 	movt	r0, #16640	; 0x4100
    200e:	f44f 3100 	mov.w	r1, #131072	; 0x20000
    2012:	6001      	str	r1, [r0, #0]
    2014:	e7ff      	b.n	2016 <Reset_Handler+0x16>
    2016:	f244 4018 	movw	r0, #17432	; 0x4418
    201a:	f2c4 1000 	movt	r0, #16640	; 0x4100
    201e:	f44f 3100 	mov.w	r1, #131072	; 0x20000
    2022:	6001      	str	r1, [r0, #0]
    2024:	e7ff      	b.n	2026 <Reset_Handler+0x26>
    2026:	e7fe      	b.n	2026 <Reset_Handler+0x26>
this does, as far as I can see, nothing. i was hoping to get some working code before having to deal with the debugger :/

i can't really read assembly, but from what I gather
movw r0, #17416 ; 0x4408
movt r0, #16640 ; 0x4100
mov.w r1, #131072 ; 0x20000
str r1, [r0, #0]
really should be doing what I want for the first instruction?? so what's going wrong??

e: also i'm uploading them the same way using the bossac bundled with the arduino IDE

gonadic io fucked around with this message at 23:22 on Jan 14, 2017

gonadic io
Feb 16, 2011

>>=
now that i think about it, it's only the c version the IDE uploads that works not my self-compiled c version.

i checked the logs though, and the arduino one does "arm-none-eabi-objcopy" -O binary "[...]/Blink2.ino.elf" "[...]Blink2.ino.bin"" too.

i'm trying to inspect the .ino.bin file that gets uploaded though (the IDE one that works), and readelf and objcopy both choke completely with
"readelf: Error: Not an ELF file - it has the wrong magic bytes at the start" and "File format not recognized"

which is weird. but i looked and the ide compiles with
arm-none-eabi-g++ -mcpu=cortex-m0plus -mthumb
which im doing so i have no loving clue what's going on

gonadic io
Feb 16, 2011

>>=
i think that i'm just missing system init maybe? that still doesn't explain why the "official" bin file won't open in objdump or readelf

gonadic io
Feb 16, 2011

>>=
the platform was wrong, i've now set it for armv6.

i do also include the vector table. you can see my c code here: https://gist.github.com/djmcgill/4e35c60449a983cb0e838e6bc59a39a0
you're totally right about getting some C working before Rust though.

i looked in the MC manual, and the clock and power for the pio are both enabled by default: http://www.atmel.com/Images/Atmel-42181-SAM-D21_Datasheet.pdf (page 145)

hannobraun is the main guide that im following, since he gets it working in rust too later on in the series.

e: and i certainly feel really over my head, i bought a bunch of relays and solenoid valves and gyros i probably won't be able to touch for a long time if ever :sigh:

e2: or i could always just say gently caress it and code in c in the arduino IDE

gonadic io fucked around with this message at 00:54 on Jan 15, 2017

gonadic io
Feb 16, 2011

>>=

hobbesmaster posted:

isn't it c++?

gnu++11 at that

gonadic io
Feb 16, 2011

>>=

Bloody posted:

I recommendreading the datasheet andunderstanding the details of whatever linker bullshit is necessary to get the rightbits of code in the right place

yeah that's prob it too. at the moment i've just blindly copied a few different linker scripts from the IDE

gonadic io
Feb 16, 2011

>>=
or i could just cheat and write my rust program as a library that the arduino IDE code calls...

gonadic io
Feb 16, 2011

>>=

yippee cahier posted:

the D21 will have a default linker script in Atmel's ASF library, somewhere. you'll also find headers that declare a bunch of resources, might help you at the rust level, might not. i don't know about how rust expects to see memory when it starts, but there will be barebones interrupt vector with enough startup code to zero out your BSS and load statically initialized variables from ROM to RAM. check it out.

I will do thanks. I'm currently trying to link against all of arduino's libs (which include the asf ones) to do the device initialisation.

Mostly so that I don't have to copy all of this into rust: https://github.com/arduino/ArduinoCore-samd/blob/master/bootloaders/zero/board_init.c

gonadic io
Feb 16, 2011

>>=
Also I'm doing this in c to start with, but rust is fine with no_std and populating a vector table.

This guy has everything working on a Due: hannobraun.de/embedded/

But the zero seems more complicated being a samd21 instead of d20.

gonadic io
Feb 16, 2011

>>=
I'm struggling to link my c program when the arduino .ino one works. Output of obj dump is:
code:
$ arm-none-eabi-objdump -Cd main.o
main.o:     file format elf32-littlearm
Disassembly of section .text._Z5setupv:
00000000 <setup()>:
   0:	4a03      	ldr	r2, [pc, #12]	; (10 <setup()+0x10>)
   2:	2380      	movs	r3, #128	; 0x80
   4:	029b      	lsls	r3, r3, #10
   6:	6013      	str	r3, [r2, #0]
   8:	4a02      	ldr	r2, [pc, #8]	; (14 <setup()+0x14>)
   a:	6013      	str	r3, [r2, #0]
   c:	4770      	bx	lr
   e:	46c0      	nop			; (mov r8, r8)
  10:	41004408 	.word	0x41004408
  14:	41004418 	.word	0x41004418
Disassembly of section .text._Z4loopv:
00000000 <loop()>:
   0:	4770      	bx	lr

$ arm-none-eabi-objdump -Cd /var/folders/bv/yr_mdf2s2cv76npqrtq40r880000gn/T/arduino_build_499341/sketch/Blink2.ino.cpp.o
/var/folders/bv/yr_mdf2s2cv76npqrtq40r880000gn/T/arduino_build_499341/sketch/Blink2.ino.cpp.o:     file format elf32-littlearm
Disassembly of section .text.setup:
00000000 <setup>:
   0:	4a03      	ldr	r2, [pc, #12]	; (10 <setup+0x10>)
   2:	2380      	movs	r3, #128	; 0x80
   4:	029b      	lsls	r3, r3, #10
   6:	6013      	str	r3, [r2, #0]
   8:	4a02      	ldr	r2, [pc, #8]	; (14 <setup+0x14>)
   a:	6013      	str	r3, [r2, #0]
   c:	4770      	bx	lr
   e:	46c0      	nop			; (mov r8, r8)
  10:	41004408 	.word	0x41004408
  14:	41004418 	.word	0x41004418
Disassembly of section .text.loop:
00000000 <loop>:
   0:	4770      	bx	lr


Linking the ino file works fine, but linking my main.c gives this error:
~/Library/Arduino15/packages/arduino/hardware/samd/1.6.11/cores/arduino/main.cpp:47: undefined reference to `setup'
~/Library/Arduino15/packages/arduino/hardware/samd/1.6.11/cores/arduino/main.cpp:51: undefined reference to `loop'

It looks to me like the distinction is the names 'setup' vs 'setup()'. But the contents of the ino file and the c file are identical so I don't understand why different labels are being generated.

gonadic io
Feb 16, 2011

>>=

Bloody posted:

looks like the top one is getting name mangled or something? section .text._z5setupv versus section .text.setup and .text._z4loopv vs .text.loop

That'll be it thanks, easy to fix too.

gonadic io
Feb 16, 2011

>>=
hurray i got the bare minimum blink.rs working on my arduino zero!(after doing it in c first)

https://github.com/djmcgill/to-the-moon/blob/master/rust/zero/src/main.rs

the only difference from my code a week ago is realising that the arduino linker scripts reference "isr_vector" not "vectors".

however i spent the whole week learning shitloads so i don't really consider it wasted time tbh.

next up: globals, properly setting up the interrupts, and a little abstraction so i'm not just writing to raw pointers

gonadic io
Feb 16, 2011

>>=
i have a terrible bit-janitor question:

copying the globals to data and zeroing bss is done like:
code:
// headers
extern unsigned int _etext;
extern unsigned int _data;
extern unsigned int _edata;
extern unsigned int _bss;
extern unsigned int _ebss;

// init function
unsigned int *src, *dst;

src = &_etext;
dst = &_data;
while (dst < &_edata) *dst++ = *src++;

dst = &_bss;
while (dst < &_ebss) *dst++ = 0;
my question is: i would expect the extern vars to be the addresses of the various locations. instead they're actual variables located in the exact locations, containing uninit data? does that mean that i have to be really careful about copying them, which would mean that i instead have a stack variable? wtf?

gonadic io
Feb 16, 2011

>>=
if it were a pointer, i could pass them as parameters to functions for example to do the initialisation there.

however since they're variables with magic addresses, i have to directly reference ONLY those variables.

the moment i pass them (by value) to a function or to a local variable, say if i wanted to rename them without editing the linker script, then that special property is lost and they're useless.

just seems like a weird way of doing it to me

gonadic io
Feb 16, 2011

>>=
I understand now what that snippet does, and why it's there (and have recreated it in rust)

The main thing i dont get is why the linker gives you variables that have the address that you want, instead of giving you the pointer values themselves. It seems to me that you'd always just immediately get the address of the data, edata etc. So why make you get their addresses, and not just... Give you the addresses? Why the extra step of indirection?

gonadic io fucked around with this message at 17:39 on Jan 21, 2017

gonadic io
Feb 16, 2011

>>=

Harik posted:

You're not thinking from the CPUs point of view. A variable is just a hard-coded address. &_data compiles to a constant.

Actually reading the value from memory is the extra step.

Just because you had to write an extra character doesn't mean more operations were performed.

Does that make sense?

that does actually, perfectly. i've been too coddled in langs that don't celebrate UB quite as much as c does.

gonadic io
Feb 16, 2011

>>=
I'm switching my code to properly use volatile read and writes, which correspond directly to the llvm instructions: http://llvm.org/docs/LangRef.html#volatile-memory-accesses

But now I have an &= in my code. Do I just have to do a read and then a write? I suppose it's not a multithreaded env so that's not a worry, but I should make sure that interrupts are disabled for it?

gonadic io
Feb 16, 2011

>>=

hobbesmaster posted:

if you have c++11 look at std::atomic and let the compiler deal with it

edit: wait, you're on a m0, you don't have strexb. disregard

also rust :q:

gonadic io
Feb 16, 2011

>>=

samd21g manual posted:

Before entering the STANDBY sleep mode the user must make sure that a significant amount of clocks
and peripherals are disabled, so that the voltage regulator is not overloaded. This is because during
STANDBY sleep mode the internal voltage regulator will be in low power mode.

i love this sentence, it's my favourite one so far.

now i feel like a hardware programmer. nothing is absolute, there are no hard limits

gonadic io
Feb 16, 2011

>>=
i think i'm doing something dumb here.

i'm trying to enable the RTC, by setting the mode, prescaler, and enable bits as instructed.

the enable bit is synced, so according to:

samd21g manual posted:

When executing an operation that requires synchronization, the Synchronization Busy bit in the Status
register (STATUS.SYNCBUSY) will be set immediately, and cleared when synchronization is complete.
The Synchronization Ready interrupt can be used to signal when synchronization is complete. This can
be accessed via the Synchronization Ready Interrupt Flag in the Interrupt Flag Status and Clear register
(INTFLAG.SYNCRDY). If an operation that requires synchronization is executed while
STATUS.SYNCBUSY is one, the bus will be stalled. All operations will complete successfully, but the
CPU will be stalled and interrupts will be pending as long as the bus is stalled.
The following bits are synchronized when written:
• Software Reset bit in the Control register (CTRL.SWRST)
• Enable bit in the Control register (CTRL.ENABLE)

i'm writing to CTRL.ENABLE and then waiting on the STATUS.SYNCBUSY bit.

code:
let rtc = RTC as *mut RTC_Mode_0;
        unsafe {
            (*rtc).control[1].set(PRESCALER);
            (*rtc).control[0].set(MODE | ENABLE);
            while (*rtc).status.get() != 0 {} // FIXME: hangs
        }
If I comment out the status check, or set it to "== 0" then the code runs as expected and i get my blinking LED.
but if i attempt to wait for the syncbusy, then it waits indefinitely.

i've checked, and clock/power to the RTC is enabled on reset. the enable bit IS set when i write to it (and is zero before). i've checked that the syncbusy bit is zero before the operation, and as far as i can tell you don't need to do anything to enable the sync functionality.

i tried setting all of my interrupts to just immediately return too, but no difference there. the syncbusy bit just...isn't set...

e: hmm, let me fiddle around with the global clock a bit

gonadic io fucked around with this message at 13:27 on Jan 26, 2017

gonadic io
Feb 16, 2011

>>=
Update: I got openocd working and managed to crash gdb a bunch

gonadic io
Feb 16, 2011

>>=

Sapozhnik posted:

You're not waiting for the SYNCBUSY bit in the STATUS register to be clear, you are waiting for the entire STATUS register to be zero.

I did an & 1 << 7 too, sync busy is the only bit on that register.

gonadic io
Feb 16, 2011

>>=

Barnyard Protein posted:

From the description, I think the syncbusy bit is clearing itself automatically after you enable the RTC, you don't need to poll on it.

e: In other words, the syncbusy may go high for a few cycles immediately after the RTC is enabled but then it goes low again before your `while (*rtc).status.get() != 0 {}` executes.

If that was low, then the loop would never iterate.

gonadic io
Feb 16, 2011

>>=

Sapozhnik posted:

post the disasm

will do.

i'm fairly sure this is something i'm doing wrong, i guess i'll go back to C until i get it working

gonadic io
Feb 16, 2011

>>=

Spatial posted:

is "rtc" marked as volatile? if not the compiler will likely transform the loop to one memory access followed by an [effectively] infinite loop.

yep it is. i even put asm!(""); in the loop but no change.

gonadic io
Feb 16, 2011

>>=

JawnV6 posted:

double post, but I took a look at the repo

you're setting up a struct with all the variable names, then instantiating it at the right base address? im assuming the [#c] bit is saying "lay this out like a dumb C POD struct, don't reorder, don't collapse". it might generate the right asm, but I'd be reluctant to give that much choice to the compiler. i'd want to know at the site I'm writing to a device exactly how wide that access will be, seems like you might specify writing to a byte-sized chunk and ending up with a 4-byte unaligned access somewhere

i wouldn't change the names at all. you have a couple deltas, more friendly names, replacing vowels, etc. idk, maybe it's just my terrible practices but if a name doesn't come up with a grep -iRl i'd probably assume it doesn't exist

that's what im doing yeah.
i have mostly been adapting the original files, so samd21g18a.h containing:
#define PORT ((Port *)0x41004400UL) /**< \brief (PORT) APB Base Address */
becomes samd21g18a.rs with:
pub const PORT: *mut PIO = 0x41004400 as *mut PIO; // (PORT) APB Base Address

and then
code:
/** \brief PortGroup hardware registers */
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
typedef struct {
  __IO PORT_DIR_Type             DIR;         /**< \brief Offset: 0x00 (R/W 32) Data Direction */
  __IO PORT_DIRCLR_Type          DIRCLR;      /**< \brief Offset: 0x04 (R/W 32) Data Direction Clear */
  __IO PORT_DIRSET_Type          DIRSET;      /**< \brief Offset: 0x08 (R/W 32) Data Direction Set */
  __IO PORT_DIRTGL_Type          DIRTGL;      /**< \brief Offset: 0x0C (R/W 32) Data Direction Toggle */
  __IO PORT_OUT_Type             OUT;         /**< \brief Offset: 0x10 (R/W 32) Data Output Value */
  __IO PORT_OUTCLR_Type          OUTCLR;      /**< \brief Offset: 0x14 (R/W 32) Data Output Value Clear */
  __IO PORT_OUTSET_Type          OUTSET;      /**< \brief Offset: 0x18 (R/W 32) Data Output Value Set */
  __IO PORT_OUTTGL_Type          OUTTGL;      /**< \brief Offset: 0x1C (R/W 32) Data Output Value Toggle */
  __I  PORT_IN_Type              IN;          /**< \brief Offset: 0x20 (R/  32) Data Input Value */
  __IO PORT_CTRL_Type            CTRL;        /**< \brief Offset: 0x24 (R/W 32) Control */
  __O  PORT_WRCONFIG_Type        WRCONFIG;    /**< \brief Offset: 0x28 ( /W 32) Write Configuration */
       RoReg8                    Reserved1[0x4];
  __IO PORT_PMUX_Type            PMUX[16];    /**< \brief Offset: 0x30 (R/W  8) Peripheral Multiplexing n */
  __IO PORT_PINCFG_Type          PINCFG[32];  /**< \brief Offset: 0x40 (R/W  8) Pin Configuration n */
       RoReg8                    Reserved2[0x20];
} PortGroup;
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
becomes
code:
pub struct PIO {
    pub dir: VolatileCell<u32>,
    pub dir_clear: VolatileCell<u32>,
    pub dir_set: VolatileCell<u32>,
    pub dir_toggle: VolatileCell<u32>,
    pub out: VolatileCell<u32>,
    pub out_clear: VolatileCell<u32>,
    pub out_set: VolatileCell<u32>,
    pub out_toggle: VolatileCell<u32>,
    pub in_: VolatileCell<u32>,
    pub control: VolatileCell<u32>,
    pub wr_config: VolatileCell<u32>,
    pub _reserved: VolatileCell<u32>,
    pub p_mux: VolatileCell<[u8; 16]>,
    pub pin_cfg: VolatileCell<[u8; 32]>,
}
where VolatileCell just enforces volatile reads and writes.

then later i'll wrap this in methods that make more sense from the outside and obey the occasional read-only/write-only register.

gonadic io fucked around with this message at 08:00 on Jan 27, 2017

gonadic io
Feb 16, 2011

>>=

JawnV6 posted:

that preserves the granularity, but in rtc.rs there's byte widths?
code:
#[repr(C)]
pub struct RTC_Mode_0 {
    pub control: [u8; 2],
    pub read_req: [u8; 2],
    pub ev_control: [u8; 2],
    pub inten_clear: u8,
    pub inten_set: u8,
    pub int_flag: u8,
    _reserved1: u8,
    pub status: u8,

are you talking about the arrays or the reserved field? that's the definition right out of the manual, but instead of a single u16 field they're bitflags so i split them up.

gonadic io
Feb 16, 2011

>>=
hmm, i think i may have broken my arduino by flashing the wrong thing. i was trying to upload using openocd and flashed the wrong section of memory.

anyway after reburning the bootloader using the ide (Amtel EDBG) i have blink.rs working again but can't load the globals anymore. i got the debugger working and it's going into the hard fault interrupt handler so i guess im making trying the stuff in http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html

is this a thing that's possible to have hosed it up? the offending code is identical to what was working before*

i checked the addresses of the linker variables:
code:
$ arm-none-eabi-nm target/arduino-zero/debug/zero
20000020 D __data_end__
20000000 D __data_start__
000026ac R __etext
* essentially just
code:
  unsigned int *src, *dst;

  src = &__etext;
  dst = &__data_start__;
  while (dst < &__data_end__)
    *dst++ = *src++;

gonadic io
Feb 16, 2011

>>=
i hope you're happy thread:

gonadic io
Feb 16, 2011

>>=
also i think i've been fundamentally misunderstanding a bunch of stuff.

how does the bootloader code interact with the binary code? does it matter if they contain the same symbols or sections? or does it just handle flashing, and the reset button etc and they don't actually share any env?

gonadic io
Feb 16, 2011

>>=

JawnV6 posted:

nice, clean wiring

i sorta prefer to color code things, at the very least vcc/ground should be red/black

same

i only got one set of wires colour coded by length. and i ain't got a snipper/stripper. hence you can see me bending the red ones at the top to the right length

gonadic io
Feb 16, 2011

>>=

meatpotato posted:

http://a.co/aMcRTWv

I "discovered" jumper wire kits in college after cutting my own for years and would never, ever go back to anything else.

i'm using pretty much that yeah. does mean that my +3.3 and ground don't have red and black though!

Adbot
ADBOT LOVES YOU

gonadic io
Feb 16, 2011

>>=
I credit these vids which are just insanely good

https://www.youtube.com/watch?v=g_1HyxBzjl0&hd=1

  • Locked thread