<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="wordpress/2.0.4" -->
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>KQK4663</title>
	<link>http://jakesprouse.net/code</link>
	<description>Hackings by Jake Sprouse</description>
	<pubDate>Wed, 23 Apr 2008 00:51:12 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.0.4</generator>
	<language>en</language>
			<item>
		<title>Integer promotion in C</title>
		<link>http://jakesprouse.net/code/2008/04/22/integer-promotion-in-c-3/</link>
		<comments>http://jakesprouse.net/code/2008/04/22/integer-promotion-in-c-3/#comments</comments>
		<pubDate>Wed, 23 Apr 2008 00:51:11 +0000</pubDate>
		<dc:creator>jake</dc:creator>
		
	<category>Uncategorized</category>
		<guid isPermaLink="false">http://jakesprouse.net/code/2008/04/22/integer-promotion-in-c-3/</guid>
		<description><![CDATA[




unsigned char t0, t1;


unsigned int diff;


&#160;


t0 = 0;


t1 = 255;


&#160;


diff = &#40;unsigned int&#41;&#40;t0 - t1&#41;;


printf&#40;"difference: %x\n", diff&#41;; 






Intuitively, I would think that the result of the subtraction would be an unsigned char with value 0x01, which then would get converted to an unsigned int with value 0x00000001 (assuming a 32-bit platform).
Instead, when the program is [...]]]></description>
			<content:encoded><![CDATA[<div class="syntax_hilite">
<div id="c-2">
<div class="c">
<ol>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;color:#3A6A8B; font-weight:bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span> t0, t1;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;color:#3A6A8B; font-weight:bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> diff;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;color:#3A6A8B; font-weight:bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;color:#3A6A8B; font-weight:bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">t0 = <span style="color: #cc66cc;color:#800000;">0</span>;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;color:#3A6A8B; font-weight:bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">t1 = <span style="color: #cc66cc;color:#800000;">255</span>;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;color:#3A6A8B; font-weight:bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">&nbsp;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;color:#3A6A8B; font-weight:bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">diff = <span style="color: #66cc66;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#40;</span>t0 - t1<span style="color: #66cc66;">&#41;</span>;</div>
</li>
<li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;color:#3A6A8B; font-weight:bold;">
<div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #000066;">printf</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">"difference: %x<span style="color: #000099; font-weight: bold;">\n</span>"</span>, diff<span style="color: #66cc66;">&#41;</span>; </div>
</li>
</ol>
</div>
</div>
</div>
<p></p>
<p>Intuitively, I would think that the result of the subtraction would be an <tt>unsigned char</tt> with value <tt>0x01</tt>, which then would get converted to an <tt>unsigned int</tt> with value 0x00000001 (assuming a 32-bit platform).</p>
<p>Instead, when the program is compiled and run, the output is:</p>
<p><tt>difference: 0xffffff01</tt></p>
<p>Turns out that my compiler is actually doing the right thing here, but C is playing tricks on me.</p>
<p>The C standard has a concept of integer <em>rank</em>.  <tt>long long</tt> has greater rank than <tt>long</tt> which has greater rank than <tt>int</tt>, which has greater rank than <tt>short</tt>, which has greater rank than <tt>char</tt>.  Unsigned types have the same rank as their signed counterparts.  Note that even if two integral types have the same <em>size</em> in bits, their rank is different.</p>
<p>For arithmetic operations, the standard specifies that operands of integral data type are promoted to integer rank when their rank is less.  Historically, there were two ways of doing so, called <em>unsigned preserving</em> and <em>value preserving</em>.  The former simply converts all unsigned <tt>char</tt>s and <tt>short</tt>s into unsigned <tt>int</tt>s.  The latter converts them to signed <tt>int</tt>s if they are smaller and unsigned <tt>int</tt>s otherwise (i.e. if <tt>short</tt> and <tt>int</tt> are the same size, unsigned <tt>short</tt>s will be converted to unsigned <tt>int</tt>s).</p>
<p>In 1974, Kernighan and Richie's <em>The C Programming Language</em> set the original C standard, and specified the unsigned preserving method.  But in 1989 the first ANSI C standard switched (after much debate) to the value preserving method, because it reduces the number of situations in which the result of an expression is <em>questionably signed</em>, meaning it could intuitively be interpreted either way.  More detail on this distinction can be found in <a href="http://www.lysator.liu.se/c/rat/c2.html#3-2">Section 3.2.1.1 of <em>Rational for the ANSI C Programming Language</em></a>.</p>
<p>So, in the example above, <tt>t0</tt> and <tt>t1</tt> are promoted to signed <tt>int</tt>s before being subtracted.  On my machine, <tt>int</tt>s are 32 bits, so the resulting difference is a signed <tt>int</tt> of value <tt>0xffffff01</tt> (-255).</p>
<p>This is then converted to <tt>unsigned int</tt>; the C standard specifies this conversion in an interesting way: "...the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type."  In a twos-complement system, this is equivalent to leaving the bits unchanged.  In our case, this means that the new value is -255 + 4294967296 = 4294967041 = <tt>0xffffff01</tt>.</p>
<p>The correct way to cause the desired wrap of in our example is to insert an explicit cast of the subtracted result back to <tt>unsigned char</tt>:</p>
<p><tt>diff = (unsigned int)(unsigned char)(t0 - t1);</tt></p>
<p>This produces the intended result:</p>
<p><tt>difference: 0x00000001</tt>
</p>
]]></content:encoded>
			<wfw:commentRSS>http://jakesprouse.net/code/2008/04/22/integer-promotion-in-c-3/feed/</wfw:commentRSS>
		</item>
		<item>
		<title>x86 Addressing and the Segment Descriptor Tables</title>
		<link>http://jakesprouse.net/code/2007/05/11/x86-addressing-and-the-segment-descriptor-tables/</link>
		<comments>http://jakesprouse.net/code/2007/05/11/x86-addressing-and-the-segment-descriptor-tables/#comments</comments>
		<pubDate>Fri, 11 May 2007 22:37:06 +0000</pubDate>
		<dc:creator>jake</dc:creator>
		
	<category>Uncategorized</category>
		<guid isPermaLink="false">http://jakesprouse.net/code/2007/05/11/x86-addressing-and-the-segment-descriptor-tables/</guid>
		<description><![CDATA[It's easy to think of a C pointer as containing a physical memory location; if my program calls int x = 42; int *p = &#038;x; printf("p = 0x%p\n", p); and the output is p = 0xdeadbeef, I could imagine that dereferencing p would actually set the address bus to 11011110101011011011111011101111 = 0xdeadbeef to read [...]]]></description>
			<content:encoded><![CDATA[<p>It's easy to think of a C pointer as containing a physical memory location; if my program calls <code>int x = 42; int *p = &#038;x; printf("p = 0x%p\n", p);</code> and the output is <tt>p = 0xdeadbeef</tt>, I could imagine that dereferencing <code>p</code> would actually set the address bus to <tt>11011110101011011011111011101111 = 0xdeadbeef</tt> to read its value.  The real story of the x86 is a bit more complicated...</p>
<p>The <a href="http://en.wikipedia.org/wiki/Intel_8086">Intel 8086</a> has a 20-bit address bus, capable of addressing one megabyte (2^20 bytes) of memory, but only a 16-bit data bus -- all registers are 16 bits wide.  To access the entire address space, then, an offset stored in one of the general purpose registers is added to the base address of a memory segment.  The segment number is stored in one of the segment registers (CS, where program code resides; SS, for the stack, DS, for global data, and ES, often used for extra program data like strings), and the final address is computed as segment*16 + offset.</p>
<p>The <a href="http://en.wikipedia.org/wiki/Intel_80286">80286</a> introduces a 16-bit <em><a href="http://my.execpc.com/~geezer/os/pm.htm">protected mode</a></em>, in which the segment registers no longer index physical locations in memory, but rather contain a 13-bit index into a table of 8-byte <em>segment descriptors</em>.  24 bits of these 8 bytes contain a base address, and physical addresses are computed by adding the offset directly to it, allowing 2^24 bytes = 16MiB to be addressed.  The descriptors also contain segment limits, allowing the kernel to detect when a piece of code addresses memory it's not supposed to.</p>
<p>There are three table types: the <em>global descriptor table</em>, the <em>interrupt descriptor table</em>, and <em>local descriptor tables</em>.  There is a single global table which is available to all processes, but each process can have its own local table.  The tables can be located anywhere in memory, and there are special instructions (<code>lgdt</code>, <code>lidt</code>, and <code>lldt</code>) for setting them up.  These instructions tell the CPU about both the location and size of the tables; since the table index from the segment register is 13 bits, the maximum table length is 8192 entries.</p>
<p>In addition to the 13-bit table index, the segment register also contains a bit which selects between the global table or the current local table (the remaining two bits specify a privilege level, which I won't discuss here).  The x86 architecture distinguishes between the <em>logical address</em> stored in the segment and offset registers visible to the programmer and the <em>linear address</em>, which the CPU forms using the segmentation table lookup.  Since we have 13 + 1 bits in the segment register used for addressing and 16 bits in the offset register, logical addresses in the 286 are 30 bits long.  The documentation for the 286 will refer to 1 GiB of <em>virtual address space</em>, which refers to logical addressing.  Of course, the address bus on the 286 is only 24 bits, and the mapping of logical addresses to linear addresses is not <a href="http://en.wikipedia.org/wiki/Bijection">bijective</a>, since the segments described in a descriptor table can overlap.  In practice, the CPU raises an exception when a segment register contains a table index which is out of bounds, and the kernel can trap this exception to retrieve the data from its virtual memory implementation.</p>
<p>When protected mode was introduced with the 80286, the old 8086-style of addressing was referred to as <em>real mode</em>.  To remain compatible with older code, the 80286 defaults to real mode and must be explicitly told to go into protected mode, from which it could not return without resetting the chip.</p>
<p>The <a href="http://en.wikipedia.org/wiki/Intel_80386">intel 80386</a> was introduced in 1985 and extended the data bus to 32 bits.  The segment registers were still 16 bits, but the segment descriptors they indexed were extended to allow 32-bit base addresses and handle 32-bit offset limits.  This scheme is known as <em>32-bit protected mode</em>.</p>
<p>Since the 386 allows each segment to address 2^32 = 4 Gib of linear address space, it is possible to set up one segment each for code and data, and not worry about segmentation; in fact, this is how the Linux kernel operates.  The nice protection features allowed by segment limits can be implemented using the hardware's paging mechanisms, which are not so x86-specific.  I'll write about paging in a future article.
</p>
]]></content:encoded>
			<wfw:commentRSS>http://jakesprouse.net/code/2007/05/11/x86-addressing-and-the-segment-descriptor-tables/feed/</wfw:commentRSS>
		</item>
		<item>
		<title>How Computers Work, Part 1 - The Keyboard Interrupt Path</title>
		<link>http://jakesprouse.net/code/2007/05/09/how-computers-work-part-1-the-keyboard-interrupt-path/</link>
		<comments>http://jakesprouse.net/code/2007/05/09/how-computers-work-part-1-the-keyboard-interrupt-path/#comments</comments>
		<pubDate>Wed, 09 May 2007 18:40:40 +0000</pubDate>
		<dc:creator>jake</dc:creator>
		
	<category>Uncategorized</category>
		<guid isPermaLink="false">http://jakesprouse.net/code/2007/05/09/how-computers-work-part-1-the-keyboard-interrupt-path/</guid>
		<description><![CDATA[I was talking with a friend the other day about questions we've used in interviews for programmers.  One of my pet peeves is the obscenely specific questions which boil down to "how would you solve this particular problem we have right now?"  If I were asked that kind of question in an interview, [...]]]></description>
			<content:encoded><![CDATA[<p>I was talking with a friend the other day about questions we've used in interviews for programmers.  One of my pet peeves is the obscenely specific questions which boil down to "how would you solve this particular problem we have right now?"  If I were asked that kind of question in an interview, I would have to wonder if the company was at all interested in the long-term effectiveness of their employees, and whether they care to let them learn and grow into more productive programmers.  Interviews need to establish a basic level of competence, but most importantly they need to determine whether the candidate knows what he doesn't know and can learn on the job.</p>
<p>On the other hand, I always tried to avoid asking questions that had nothing to do with programming, such as <a href="http://blogs.msdn.com/bgroth/archive/2004/09/27/235071.aspx">why are manhole covers round?</a>.  My friend told me of a great one he uses: "if I type '<code>ping www.kqk4663.com</code>' into a terminal at a UNIX box and hit return, what happens?"  It's been a while since I thought about the low-level hardware and software that makes <code>ping</code> possible, so as an exercise I wrote up the chain of events that occur on an 80x86 box running Linux:</p>
<p><img src="http://jakesprouse.net/wordpress/wp-content/uploads/2007/05/wordpress/images/8059.png" border="0" height="51" width="60" alt="8059.png" align="right" />A typical <a href="http://www.computer-engineering.org/ps2protocol/">PS/2</a> keyboard has an onboard chip which detects keypresses and handles things like "debouncing" the switches.  Different keyboards have different key mechanisms, so there is a variety of encoder chips (the <a href="http://en.wikipedia.org/wiki/Intel_8048">Intel 8048</a> was popular early on), but the end result is a serial data signal sent through the keyboard cord to a controller chip on the motherboard.  This data consists of (possibly multibyte) sequences called "<a href="http://www.computer-engineering.org/ps2keyboard/scancodes2.html">scan codes</a>."  For each key there are two unique scan codes: a "make code" which is sent when the key is pressed, and a "break code" which is sent when it is released.</p>
<p>The controller chip is an Intel 8042-compatible device which handles decoding the serial stream from the keyboard, and telling the CPU about keypresses.  It may also be integrated into the motherboard's chipset.  The CPU talks to it via I/O space addressing at locations 0x60 (data buffer) and 0x64 (status/commands).</p>
<p>A typical PC contains two <a href="http://en.wikipedia.org/wiki/8259A">8259 Programmable Interrupt Controller</a> chips (which may be part of a <a href="http://en.wikipedia.org/wiki/Southbridge_%28computing%29">chipset</a>), or one of it's <a href="http://en.wikipedia.org/wiki/Intel_APIC_Architecture">more advanced</a> descendants.<br />
<img src="http://jakesprouse.net/wordpress/wp-content/uploads/2007/05/wordpress/images/8259.png" border="0" height="340" width="300" alt="8259.png" align="left" /><br />
The first one which controls the <tt>INT</tt> line of the CPU.  It has eight interrupt inputs, <tt>IRQ0</tt> - <tt>IRQ7</tt>, each of which can be hooked up to a peripheral device, one of which is the second "cascaded" 8259 on <tt>IRQ2</tt>.  This allows for fifteen devices to interrupt the PC.  The keyboard controller's interrupt line is connected to IRQ1.</p>
<p>When the <tt>INT</tt> line is raised, the processor immediately pushes the flags register and a return address onto the stack.  It then fetches the interrupt vector (which IRQ line generated the interrupt) by lowering the INTA line to retrieve it from the 8259.</p>
<p>Given the IRQ vector, it CPU must look up the address of the code which will handle the interrupt (a.k.a. the <em>interrupt service routine</em>, or ISR).  On the original 8086, the CPU expects to find a table of 32-bit ISR pointers at address 0x0.  But the 80286 and later processors have a register called <code>idtr</code>, which points to the system's <em>interrupt descriptor table</em> in memory.  Either way, the CPU uses the IRQ number as an index into these tables, retrieves a pointer to the ISR, pushes the flags register onto the stack, and jumps to that address. The ISR will return using the <code>IRET</code> instruction rather than <code>RET</code> to tell the processor to pop the flags register.</p>
<p>Now, assuming that it set up the interrupt vector table properly at bootup, the kernel software is handling the interrupt.  Next post, I'm going to dig into the Linux kernel and find out how it deals.
</p>
]]></content:encoded>
			<wfw:commentRSS>http://jakesprouse.net/code/2007/05/09/how-computers-work-part-1-the-keyboard-interrupt-path/feed/</wfw:commentRSS>
		</item>
		<item>
		<title>Debugging Apple&#8217;s LoginItemsAE.c on Intel Macs</title>
		<link>http://jakesprouse.net/code/2007/03/28/debugging-apples-loginitemsaec-on-intel-macs/</link>
		<comments>http://jakesprouse.net/code/2007/03/28/debugging-apples-loginitemsaec-on-intel-macs/#comments</comments>
		<pubDate>Thu, 29 Mar 2007 00:43:31 +0000</pubDate>
		<dc:creator>jake</dc:creator>
		
	<category>Uncategorized</category>
		<guid isPermaLink="false">http://jakesprouse.net/code/?p=5</guid>
		<description><![CDATA[Came across some wackiness involving the Apple Event Manager and LoginItemsAE.c (Apple's example code for manipulating a user's list of login items).  In hopes that others dealing with similar problems will find this:
The "System Events" process can be accessed via an Apple Event (i.e. Applescript) dictionary which allow you to list, add, and delete [...]]]></description>
			<content:encoded><![CDATA[<p>Came across some wackiness involving the Apple Event Manager and <tt>LoginItemsAE.c</tt> (Apple's <a href="http://developer.apple.com/samplecode/LoginItemsAE/index.html">example code</a> for manipulating a user's list of login items).  In hopes that others dealing with similar problems will find this:</p>
<p>The "System Events" process can be accessed via an Apple Event (<em>i.e.</em> Applescript) dictionary which allow you to list, add, and delete login items.  Login items are described by two parameters, a path to the application to be launched when the user logs in, and a flag which specifies whether it should be hidden once launched.</p>
<p>In <tt>LoginItemsAE.c</tt>, retrieved login item paths are encoded as <tt>typeUnicodeText</tt> (native-endian UTF-16) and are converted to CFURLs for the user's consumption, in three steps:</p>
<ol>
<li>Get a UTF8-converted string in a char buffer using <tt>AEGetKeyPtr</tt> (<tt>#defined</tt> to <tt>AEGetParam</tt>) to "coerce" the parameter to typeUTF8Text.</li>
<li>Make an <tt>FSPathRef</tt> from the character buffer using <tt>FSPathMakeRef</tt>.</li>
<li>Use <tt>CFURLCreateFromFileSystemRepresentation</tt> to get a <tt>CFURLRef</tt></li>
</ol>
<p>On my Powerbook (PPC), this worked fine, but when I sent my beta to testers, MacBook users reported that my "Start At Login" option wasn't working.  In debugging, I found that <tt>AEGetKeyPtr</tt> was returning <tt>errAECoercionFail</tt> (-1700) -- it couldn't convert the Unicode path to UTF8.</p>
<p>A symmetric problem occurs when asking System Events to add a login item.  Here, <tt>LoginItemsAE.c</tt> uses <tt>AECoercePtr</tt> to create it's path property in <tt>typeUnicode</tt> form from a UTF8 character buffer, but was seeing <tt>errAECoercionFail</tt> on Intel Macs.</p>
<p>Well, that sucks.  In Apple's <a href="http://developer.apple.com/documentation/AppleScript/Conceptual/AppleEvents/appendix3_aepg/chapter_12_section_1.html"><em>Apple Events Programming Guide</em></a>, it says</p>
<blockquote><p>
Support for the following coercions was added in Mac OS X version 10.4:</p>
<p>Between these types:</p>
<ul>
<li><tt>typeStyledText, typeUnicodeText, typeUTF8Text, and typeUTF16ExternalRepresentation</tt></li>
<p>...
</ul>
</blockquote>
<p>In searching for a solution on the web, I noticed that the <a href="http://growl.info">Growl</a> project also uses <tt>LoginItemsAE.c</tt> to set their helper app as a login item.  The latest repository version shows no changes.  Have they not seen the same problem?</p>
<p>The workaround: instead of asking AE to coerce our data for us, we do it ourselves using <tt>CFString</tt>s.  I wrote a function which converts a char buffer between CF string representations:</p>
<pre>static int ConvertPathEncoding(char *path, int pathlen, int bufsize, int src_cftype, int dest_cftype)
{
  int ret = 0;
  CFStringRef cfpath = CFStringCreateWithBytes(NULL, (UInt8 *)path, pathlen, src_cftype, FALSE);
  if (cfpath == NULL) {
    ret = -1;
  }
  else {
    int len = CFStringGetLength(cfpath);
    Size actualLen;
    CFStringGetBytes(cfpath, CFRangeMake(0, len), dest_cftype, 0, false,
                    (UInt8 *)path, bufsize, &#038;actualLen);
    CFRelease(cfpath);
    ret = (int)actualLen;
  }
  return ret;
}</pre>
<p>Now I can ask <tt>AEGetKeyPtr</tt> to give me a <tt>typeUnicode</tt> string in my buffer, and then call <tt>ConvertPathEncoding</tt> to convert from <tt>kCFStringEncodingUnicode</tt> and <tt>kCFStringEncodingUTF8</tt>.  Similarly, instead of calling <tt>AECoercePtr</tt> to create my login item path parameter description, I call <tt>ConvertPathPathEncoding</tt> to convert from <tt>kCFStringEncodingUTF8</tt> and <tt>kCFStringEncodingUnicode</tt>.</p>
<p>In <tt>AEDataModel.h</tt>, we see that <tt>typeUnicodeText</tt> and some other types are "<tt>deprecated due to their lack of explicit encoding or byte order definition.  Please use typeUTF16ExternalRepresentation or typeUTF8Text instead.</tt>"  <tt>typeUTF16ExternalRepresentation</tt> is defined as "<tt>big-endian 16 bit unicode with optional byte-order-mark, or little-endian 16 bit unicode with required byte-order-mark,</tt>", <em>i.e.</em> it is possible to check if the string has the correct byte-order for the system.   But apparently, the System Events API is still using UTf-16 with no BOM.</p>
]]></content:encoded>
			<wfw:commentRSS>http://jakesprouse.net/code/2007/03/28/debugging-apples-loginitemsaec-on-intel-macs/feed/</wfw:commentRSS>
		</item>
		<item>
		<title>I needed an outlet</title>
		<link>http://jakesprouse.net/code/2007/03/28/i-needed-an-outlet/</link>
		<comments>http://jakesprouse.net/code/2007/03/28/i-needed-an-outlet/#comments</comments>
		<pubDate>Wed, 28 Mar 2007 22:14:45 +0000</pubDate>
		<dc:creator>jake</dc:creator>
		
	<category>Uncategorized</category>
		<guid isPermaLink="false">http://jakesprouse.net/code/?p=4</guid>
		<description><![CDATA[Lately, I've had a lot to say about my hacking, mostly regarding Cocoa/OSX, but not excluding general technology.  I didn't want to clutter up Rhythm &#038; Gravy with ramblings about work, so instead you will find them here.  Enjoy!

]]></description>
			<content:encoded><![CDATA[<p>Lately, I've had a lot to say about my hacking, mostly regarding Cocoa/OSX, but not excluding general technology.  I didn't want to clutter up <a href="http://www.jakesprouse.net">Rhythm &#038; Gravy</a> with ramblings about work, so instead you will find them here.  Enjoy!
</p>
]]></content:encoded>
			<wfw:commentRSS>http://jakesprouse.net/code/2007/03/28/i-needed-an-outlet/feed/</wfw:commentRSS>
		</item>
	</channel>
</rss>
