I thought of this one a long time ago, but have only just got around to throwing together the pixels.
Friday 16 December 2011
One-Liners
For the last few weeks, Martyn Honeyford and I have been trying to come up with the shortest joke.
I think it's going to be difficult to beat:
Martyn came up with:
From Jimmy Carr:
I think it's going to be difficult to beat:
"Pretentious? Moi?"This has been variously attributed to Fawlty Towers and The Muppet Show, though the precise origin is unclear.
Martyn came up with:
"German sausage is the wurst."This has apparently been independently 'discovered' many times.
From Jimmy Carr:
"Venison's dear!"From elsewhere on the web:
"Stationery store moves."But perhaps my favourite sources of surprisingly good one-liners are the Carry On films. In Carry On Up The Khyber (1968):
"Dwarf shortage."
"Small medium at large."
"Putting 'sexy' into 'dyslexia'."
"Poor old Will! Why do they always fire at him?"And, most famously, Carry on Cleo (1964) via Take It From Here on BBC radio:
"Infamy, infamy. They've all got it in for me!"
Monday 28 November 2011
Surface-Mount Printed Circuit Board Pen
There's a retired gentleman just around the corner in our village who makes "hand crafted writing implements." As well as some beautiful turned-wood examples, he makes pens from surface-mounted PCBs. I've never seen anything quite like them. He heats a section of board and curls it into a tube, which he then encases in clear acrylic. He turns and polishes this to produce a pen shaft. In his shed.
Makes a great present for geeks. Not surprisingly, my girlfriend not-so-secretly bought one from the village craft fair at the weekend. Hurrah for Christmas!
There's probably something profound about making an old-fashioned pen from new-fangled technology. Probably.
The photos don't do it justice: the components are in glorious 3D in real life.
There's probably something profound about making an old-fashioned pen from new-fangled technology. Probably.
Sunday 4 September 2011
Saturday 2 July 2011
ZX Spectrum Fine Art 2
Following the resounding critical success of my last piece of ZX Spectrum Fine Art ("I rather like it"), I bring you an interpretation of "The Billy Boys" by Jack Vettriano. See if you can tell which is the original:
Saturday 11 June 2011
Comparison Operand Stripping in C++
If you're writing C++ assertions or unit tests, you'll have written code similar to:
ASSERT(a < b);
or
if (!VERIFY(a != b)) { ... }If you're running without a debugger attached or with optimisations fully on, you'll get the usual error report, but no idea what the underlying values are or were:
test.cpp(1): Failed VERIFY(a != b)Wouldn't it be wonderful if it did? Up until recently, I thought the only way of achieving that was to use ugly macros of the forms:
ASSERT_OP(a, <, b);which, if you're lucky, will produce output similar to:
ASSERT_LT(a, b);
test.cpp(2): Failed ASSERT(a < b)The only problem is that it's extremely ugly. There must be a better way, surely? But it's C++: there's always an alternative, no matter how convoluted internally. Consider operator precedence:
'a' is 321
'b' is 123
something << a < b >> somethingThis will evaluate 'something << a' first, then 'b >> something', and then apply the less-than operation to the results. Now consider:
namespace Verify
{
struct Outer;
template<typename T>
struct Inner
{
const T& value;
explicit Inner(const T& inner) :
value(inner) {}
};
template<typename T>
Inner<T> operator<<(const Outer&,
const T& inner)
{
return Inner<T>(inner);
}
template<typename T>
Inner<T> operator>>(const T& inner,
const Outer&)
{
return Inner<T>(inner);
}
}
Note that 'Outer' is not defined, only declared; we're only going to use its type information:
#define CHILLIANT_OUTER \
(*(Verify::Outer*)nullptr)
#define CHILLIANT_VERIFY(expr) /
CHILLIANT_BLAH(CHILLIANT_OUTER << expr >>\
CHILLIANT_OUTER)
But what's the defintion of 'CHILLIANT_BLAH()'? Well, we want to report the location of any failure along with the expression text:
#define CHILLIANT_VERIFY(expr) /
(Verify::Report(__FILE__,__LINE__,#expr, \
(CHILLIANT_OUTER << expr >>\
CHILLIANT_OUTER)))
The type of the final argument to 'Verify::Report()' can be one of three things:
- The result of a binary comparison (including encapsulations of the two operand values),
- A single value (and an encapsulation of that value) that can be utilised as a Boolean condition, and
- A Boolean value (for cases where we cannot easily determine the encapsulations).
The third case should be redundant, but for simplicity we're not going to handle complex cases such as 'a < b && c > d' here. That's left as an exercise for the reader.
namespace Verify
{
template<typename L,typename R>
bool Report(/*blah*/,
const Binary<L,R>& result)
{
if (!result.condition)
{
/*blah*/
}
return result.condition;
}
template<typename T>
bool Report(/*blah*/,
const Unary<T>& result)
{
if (!result.condition)
{
/*blah*/
}
return result.condition;
}
bool Report(/*blah*/,
bool result)
{
if (!result)
{
/*blah*/
}
return result;
}
}
Now all that remains is the code to glue the three condition types to the three reporting functions. Firstly, binary comparison operations:
namespace Verify
{
template<typename L,typename R>
struct Binary
{
bool condition;
const char* operation;
const Inner<L>& lhs;
const Inner<R>& rhs;
Binary(bool result,
const char* op,
const Inner<L>& left,
const Inner<R>& right) :
condition(result),
operation(op),
lhs(left),
rhs(right) {}
};
template<typename L,typename R>
Binary<L,R> operator<(const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value < rhs.value,
"<", lhs, rhs);
}
/* Similarly for <=, ==, !=, > and >= */
}
This will be invoked for expressions such as 'x < y' and even 'x*2 < y+1'. But we also want to handle expressions without comparison operators:
namespace Verify
{
template<typename T>
struct Unary
{
bool condition;
const Inner<T>& value;
Unary(bool result,
const Inner<T>& inner) :
condition(result),
value(inner) {}
};
template<typename T>
Unary<T> operator>>(const Inner<T>& inner,
const Outer&)
{
return Unary<T>(inner.value, inner);
}
}
Finally, we must deal with expressions that use operators that "fall between the gaps" (i.e. have lower precedence than the comparison operators, such as '&&'). A good catch-all is to allow 'Inner<T>' to implicitly convert to its underlying type 'T':
namespace Verify
{
template<typename T>
struct Inner
{
const T& value;
explicit Inner(const T& inner) :
value(inner) {}
operator T() const { return value; }
};
}
Adding type-safe helper functions to help with reporting and putting everything together, we end up with the following:
namespace Verify
{
void Output(const char* fmt, ...);
struct Outer;
template<typename T>
struct Inner
{
const T& value;
explicit Inner(const T& inner) :
value(inner) {}
operator T() const { return value; }
void Output(const char* label) const;
};
template<typename T>
Inner<T> operator<<(const Outer&,
const T& inner)
{
return Inner<T>(inner);
}
template<typename T>
Inner<T> operator>>(const T& inner,
const Outer&)
{
return Inner<T>(inner);
}
template<typename T>
struct Unary
{
bool condition;
const Inner<T>& value;
Unary(bool result,
const Inner<T>& inner) :
condition(result),
value(inner) {}
};
template<typename T>
Unary<T> operator>>(const Inner<T>& inner,
const Outer&)
{
return Unary<T>(inner.value, inner);
}
template<typename L,typename R>
struct Binary
{
bool condition;
const char* operation;
const Inner<L>& lhs;
const Inner<R>& rhs;
Binary(bool result, const char* op,
const Inner<L>& left,
const Inner<R>& right) :
condition(result),
operation(op),
lhs(left),
rhs(right) {}
};
template<typename L,typename R>
Binary<L,R> operator<(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value < rhs.value,
"<", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator<=(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value <= rhs.value,
"<=", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator>(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value > rhs.value,
">", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator>=(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value >= rhs.value,
">=", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator==(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value == rhs.value,
"==", lhs, rhs);
}
template<typename L,typename R>
Binary<L,R> operator!=(
const Inner<L>& lhs,
const Inner<R>& rhs)
{
return Binary<L,R>(
lhs.value != rhs.value,
"!=", lhs, rhs);
}
bool Report(const char* file, long line,
const char* expression,
bool result)
{
if (!result)
{
Output("%s(%ld): Failed "
"CHILLIANT_VERIFY(%s)\n",
file, line, expression);
Inner<bool> value(false);
value.Output("value");
}
return result;
}
template<typename T>
bool Report(const char* file, long line,
const char* expression,
const Unary<T>& result)
{
if (!result.condition)
{
Output("%s(%ld): Failed "
"CHILLIANT_VERIFY(%s)\n",
file, line, expression);
result.value.Output("value");
}
return result.condition;
}
template<typename L,typename R>
bool Report(const char* file, long line,
const char* expression,
const Binary<L,R>& result)
{
if (!result.condition)
{
Output("%s(%ld): Failed "
"CHILLIANT_VERIFY(%s)\n",
file, line, expression);
Output(" for "
"(LHS %s RHS)"
" where\n",
result.operation);
result.lhs.Output("LHS");
result.rhs.Output("RHS");
}
return result.condition;
}
}
#define CHILLIANT_VERIFY_OUTER \
(*(Verify::Outer*)nullptr)
#define CHILLIANT_VERIFY(expr) \
(Verify::Report(__FILE__,__LINE__,#expr,\
(CHILLIANT_VERIFY_OUTER << expr >>\
CHILLIANT_VERIFY_OUTER)))
Now we can write reporters (including for user-defined types) and perform checks such as the following:
template<>
void Verify::Inner<bool>::
Output(const char* label) const
{
Verify::Output(" %s is %s (bool)\n",
label,
value ? "true" : "false");
}
template<>
void Verify::Inner<int> :: Output(const char* label) const
{
Verify::Output(" %s is %d (int)\n",
label,
value);
}
template<>
void Verify::Inner<float> :: Output(const char* label) const
{
Verify::Output(" %s is %g (float)\n",
label,
value);
}
void TestVerify(void)
{
int i = 3;
float f = 3.14159f;
if (!CHILLIANT_VERIFY(false)) ...
if (!CHILLIANT_VERIFY(i > 0 && i < 2)) ...
if (!CHILLIANT_VERIFY(12 * 6 < i + 7)) ...
if (!CHILLIANT_VERIFY(12 * 6 > f * 1000)) ...
}
This will produce the following output:
test.cpp(101): Failed CHILLIANT_VERIFY(false)
value is false (bool)
test.cpp(102): Failed CHILLIANT_VERIFY(i > 0 && i < 2)
value is false (bool)
test.cpp(103): Failed CHILLIANT_VERIFY(12 * 6 < i + 7)
for CHILLIANT_VERIFY(LHS < RHS) where
LHS is 72 (int)
RHS is 10 (int)
test.cpp(104): Failed CHILLIANT_VERIFY(12 * 6 > f * 1000)
for CHILLIANT_VERIFY(LHS > RHS) where
LHS is 72 (int)
RHS is 3141.59 (float)
The nice thing about this "comparison operand stripping" pattern is that you can turn the feature on and off at will, without jumping through syntactic hoops:
#define CHILLIANT_VERIFY(expr) (expr)
It can also be extended to assertions. What's not to like?
Saturday 21 May 2011
static_assert and the C++ Preprocessor
With a title like that, you know you're in for a bit of geekiness.
With C++0x we now have proper static asserts. Hurrah! Except I don't like them. Their syntax is:
static_assert(sizeof(int) < 5, "Integers too big");
To me, the second argument is extra fluff that should be optional, but isn't. Perhaps we can get around it with preprocessor macros:
STATIC_ASSERT(sizeof(int) < 5, "Integers too big");
STATIC_ASSERT(sizeof(int) < 5);
But how do we overload preprocessor macros? With the help of variadic macros it is (just about) possible to overload based on the number of arguments. Ironically, this is much easier with the C99 preprocessor than with the current C++ one; all the solutions for C++ that I've seen need tweaking for different compilers. The code below works for Microsoft C++ 2010.
As an aside, Jens Gustedt has produced an amazing (and instructive) set of C99 preprocessor macros called P99. Well worth a look.
But for our C++ case, we want to construct something along the lines of:
#define STATIC_ASSERT(condition, ...) \
if (__VA_ARGS__ is empty) then \
static_assert(condition, #condition) \
else \
static_assert(condition, __VA_ARGS__)
There are two problems here: (a) how to determine if there are additional arguments, and (b) achieving an if-then-else structure in a macro that's fully resolved by the preprocessor.
The first problem can be solved using a technique I first saw in a reply to a question on stackoverflow:
#define CHILLIANT_LITERAL(...) __VA_ARGS__
#define CHILLIANT_VA_PICK__(a,b,c,d,e,f,g,h,i,j,...) j
#define CHILLIANT_VA_PICK_(a) \
CHILLIANT_LITERAL(CHILLIANT_VA_PICK__)a
#define CHILLIANT_VA_PICK(a,b,...) \
CHILLIANT_VA_PICK_((a##__VA_ARGS__##b,a##b))
#define CHILLIANT_VA_EMPTY__YTPME_AV_TNAILLIHC 0,0,0,0,0,0,0,0,0,1
#define CHILLIANT_VA_EMPTY(...) \
CHILLIANT_VA_PICK(CHILLIANT_VA_EMPTY_, \
_YTPME_AV_TNAILLIHC,__VA_ARGS__)
How this actually works is left as an exercise for the reader, but the bottom line is that "CHILLIANT_VA_EMPTY(...)" called with no arguments resolves to the token "1", whilst one or more arguments causes it to resolve to "0". The above code snippet only works for up to nine arguments, but it is trivial to expand to a greater number.
For completeness, here's the macro to return the count of the number of arguments:
#define CHILLIANT_VA_COUNT__TNUOC_AV_TNAILLIHC 9,8,7,6,5,4,3,2,1,0
#define CHILLIANT_VA_COUNT(...) \
CHILLIANT_VA_PICK(CHILLIANT_VA_COUNT_, \
_TNUOC_AV_TNAILLIHC,__VA_ARGS__)
Groovy, huh?
The second problem, generating the if-then-else semantic, is made difficult because we want it to resolve during the preprocessor phase, not during compilation. However, if we assume the condition is only ever the token "0" or "1" (which it is) the problem can be solved with careful identifier concatenation:
#define CHILLIANT_IGNORE(...)
#define CHILLIANT_IF_0(...) CHILLIANT_LITERAL
#define CHILLIANT_IF_1(...) __VA_ARGS__ CHILLIANT_IGNORE
#define CHILLIANT_IF(condition) \
CHILLIANT_LITERAL(CHILLIANT_IF_)##CHILLIANT_LITERAL(condition)
This is used as follows:
CHILLIANT_IF(condition)(then_clause)(else_clause)
Now, we can construct the definition of "STATIC_ASSERT()":
#define STATIC_ASSERT(condition, ...) \
static_assert(condition, \
CHILLIANT_IF(CHILLIANT_VA_EMPTY(__VA_ARGS__)) \
(#condition) \
(__VA_ARGS__))
This does seem to be a lot of trouble to go to just to make the second argument of a static assert optional, but now you also have some of the basic building blocks to really go to town with the preprocessor.
With C++0x we now have proper static asserts. Hurrah! Except I don't like them. Their syntax is:
static_assert(sizeof(int) < 5, "Integers too big");
To me, the second argument is extra fluff that should be optional, but isn't. Perhaps we can get around it with preprocessor macros:
STATIC_ASSERT(sizeof(int) < 5, "Integers too big");
STATIC_ASSERT(sizeof(int) < 5);
But how do we overload preprocessor macros? With the help of variadic macros it is (just about) possible to overload based on the number of arguments. Ironically, this is much easier with the C99 preprocessor than with the current C++ one; all the solutions for C++ that I've seen need tweaking for different compilers. The code below works for Microsoft C++ 2010.
As an aside, Jens Gustedt has produced an amazing (and instructive) set of C99 preprocessor macros called P99. Well worth a look.
But for our C++ case, we want to construct something along the lines of:
#define STATIC_ASSERT(condition, ...) \
if (__VA_ARGS__ is empty) then \
static_assert(condition, #condition) \
else \
static_assert(condition, __VA_ARGS__)
There are two problems here: (a) how to determine if there are additional arguments, and (b) achieving an if-then-else structure in a macro that's fully resolved by the preprocessor.
The first problem can be solved using a technique I first saw in a reply to a question on stackoverflow:
#define CHILLIANT_LITERAL(...) __VA_ARGS__
#define CHILLIANT_VA_PICK__(a,b,c,d,e,f,g,h,i,j,...) j
#define CHILLIANT_VA_PICK_(a) \
CHILLIANT_LITERAL(CHILLIANT_VA_PICK__)a
#define CHILLIANT_VA_PICK(a,b,...) \
CHILLIANT_VA_PICK_((a##__VA_ARGS__##b,a##b))
#define CHILLIANT_VA_EMPTY__YTPME_AV_TNAILLIHC 0,0,0,0,0,0,0,0,0,1
#define CHILLIANT_VA_EMPTY(...) \
CHILLIANT_VA_PICK(CHILLIANT_VA_EMPTY_, \
_YTPME_AV_TNAILLIHC,__VA_ARGS__)
How this actually works is left as an exercise for the reader, but the bottom line is that "CHILLIANT_VA_EMPTY(...)" called with no arguments resolves to the token "1", whilst one or more arguments causes it to resolve to "0". The above code snippet only works for up to nine arguments, but it is trivial to expand to a greater number.
For completeness, here's the macro to return the count of the number of arguments:
#define CHILLIANT_VA_COUNT__TNUOC_AV_TNAILLIHC 9,8,7,6,5,4,3,2,1,0
#define CHILLIANT_VA_COUNT(...) \
CHILLIANT_VA_PICK(CHILLIANT_VA_COUNT_, \
_TNUOC_AV_TNAILLIHC,__VA_ARGS__)
Groovy, huh?
The second problem, generating the if-then-else semantic, is made difficult because we want it to resolve during the preprocessor phase, not during compilation. However, if we assume the condition is only ever the token "0" or "1" (which it is) the problem can be solved with careful identifier concatenation:
#define CHILLIANT_IGNORE(...)
#define CHILLIANT_IF_0(...) CHILLIANT_LITERAL
#define CHILLIANT_IF_1(...) __VA_ARGS__ CHILLIANT_IGNORE
#define CHILLIANT_IF(condition) \
CHILLIANT_LITERAL(CHILLIANT_IF_)##CHILLIANT_LITERAL(condition)
This is used as follows:
CHILLIANT_IF(condition)(then_clause)(else_clause)
Now, we can construct the definition of "STATIC_ASSERT()":
#define STATIC_ASSERT(condition, ...) \
static_assert(condition, \
CHILLIANT_IF(CHILLIANT_VA_EMPTY(__VA_ARGS__)) \
(#condition) \
(__VA_ARGS__))
This does seem to be a lot of trouble to go to just to make the second argument of a static assert optional, but now you also have some of the basic building blocks to really go to town with the preprocessor.
Saturday 16 April 2011
DAB Clock Radio Mod
I recently took the (somewhat conservative) plunge and went shopping for a DAB clock radio for my beside table. They all seemed rather expensive for what you get, except for one model which my local "evil" supermarket was selling off for £10. No-one at the store seemed to know why they were selling it off for a fraction of its retail price, so I snapped one up on the off-chance.
After getting it home, it quickly became apparent that there was a fundamental design flaw: the backlight is far too bright for a bedside table, even on its "dim" setting. This seems to be a common complaint with many new DAB clock radios. But with just a screwdriver and long-nosed pliers there is a solution...
The following instructions are for the "Technika CR-109IDAB" device, but I'm guessing they're all pretty much of a pattern. Your warrantee will be invalidated, of course.
After getting it home, it quickly became apparent that there was a fundamental design flaw: the backlight is far too bright for a bedside table, even on its "dim" setting. This seems to be a common complaint with many new DAB clock radios. But with just a screwdriver and long-nosed pliers there is a solution...
The following instructions are for the "Technika CR-109IDAB" device, but I'm guessing they're all pretty much of a pattern. Your warrantee will be invalidated, of course.
- Remove the six crosshead screws from the base.
- Remove the two crosshead screws holding the LCD module to the front panel.
- Replace (or add) opaque electrician's tape over the LEDs so only a fraction of their light will leak into the prism behind the LCD module from the side.
- Reassemble the unit, in reverse order. The brightness of the backlight should be considerably reduced, perhaps to the point where you won't be able to easily read the time in daylight; but at least you'll be able to sleep at night without eye-shades.
Thursday 24 March 2011
RGB/HSV in HLSL 3
DDRKirby(ISQ) had problems with the HLSL code for "RGBtoHSV" in my original posting on this subject. I've had a chance to look at this in more detail and believe I know what the problem is/was. Some versions of the HLSL compiler have difficulty generating correct code for component swizzling under very specific circumstances. A minor refactoring seems to solve the problem:
float3 Hue(float H)
{
float R = abs(H * 6 - 3) - 1;
float G = 2 - abs(H * 6 - 2);
float B = 2 - abs(H * 6 - 4);
return saturate(float3(R,G,B));
}
float3 HSVtoRGB(in float3 HSV)
{
return ((Hue(HSV.x) - 1) * HSV.y + 1) * HSV.z;
}
float3 RGBtoHSV(in float3 RGB)
{
float3 HSV = 0;
#if NO_ASM
HSV.z = max(RGB.r, max(RGB.g, RGB.b));
float M = min(RGB.r, min(RGB.g, RGB.b));
float C = HSV.z - M;
#else
float4 RGB4 = RGB.rgbr;
asm { max4 HSV.z, RGB4 };
asm { max4 RGB4.w, -RGB4 };
float C = HSV.z + RGB4.w;
#endif
if (C != 0)
{
float4 RGB0 = float4(RGB, 0);
float4 Delta = (HSV.z - RGB0) / C;
Delta.rgb -= Delta.brg;
Delta.rgb += float3(2,4,6);
Delta.brg = step(HSV.z, RGB) * Delta.brg;
#if NO_ASM
HSV.x = max(Delta.r, max(Delta.g, Delta.b));
#else
float4 Delta4 = Delta.rgbr;
asm { max4 HSV.x, Delta4 };
#endif
HSV.x = frac(HSV.x / 6);
HSV.y = 1 / Delta.w;
}
return HSV;
}
The major change (which produces the same optimized code as I expected from the original version) is highlighted. I'm not saying this will fix everyone's woes, but it certainly corrects the problem with the one situation I came across.
float3 Hue(float H)
{
float R = abs(H * 6 - 3) - 1;
float G = 2 - abs(H * 6 - 2);
float B = 2 - abs(H * 6 - 4);
return saturate(float3(R,G,B));
}
float3 HSVtoRGB(in float3 HSV)
{
return ((Hue(HSV.x) - 1) * HSV.y + 1) * HSV.z;
}
float3 RGBtoHSV(in float3 RGB)
{
float3 HSV = 0;
#if NO_ASM
HSV.z = max(RGB.r, max(RGB.g, RGB.b));
float M = min(RGB.r, min(RGB.g, RGB.b));
float C = HSV.z - M;
#else
float4 RGB4 = RGB.rgbr;
asm { max4 HSV.z, RGB4 };
asm { max4 RGB4.w, -RGB4 };
float C = HSV.z + RGB4.w;
#endif
if (C != 0)
{
float4 RGB0 = float4(RGB, 0);
float4 Delta = (HSV.z - RGB0) / C;
Delta.rgb -= Delta.brg;
Delta.rgb += float3(2,4,6);
Delta.brg = step(HSV.z, RGB) * Delta.brg;
#if NO_ASM
HSV.x = max(Delta.r, max(Delta.g, Delta.b));
#else
float4 Delta4 = Delta.rgbr;
asm { max4 HSV.x, Delta4 };
#endif
HSV.x = frac(HSV.x / 6);
HSV.y = 1 / Delta.w;
}
return HSV;
}
The major change (which produces the same optimized code as I expected from the original version) is highlighted. I'm not saying this will fix everyone's woes, but it certainly corrects the problem with the one situation I came across.
Saturday 19 March 2011
Slacker By Nature
Having a blogger motto of "Software engineer by trade; slacker by nature" may not seem like a good advert for myself, but I believe I'm incredibly effective. That's subtly difference from being efficient, and vastly different from being a fast coder; I probably wouldn't fair well in demoscene 48-hour coding challenges.
But being a slacker by nature means I'm more likely to look for a simple, path-of-least-resistance solution, which, in many software engineering domains is the "right" solution. Don't reinvent the wheel ... unless you're looking for a competitive advantage by engineering a racing wheel that is 1% more efficient for 100% extra work. In which case, reimplement your string class and XML parser while you're at it!
But being a slacker by nature means I'm more likely to look for a simple, path-of-least-resistance solution, which, in many software engineering domains is the "right" solution. Don't reinvent the wheel ... unless you're looking for a competitive advantage by engineering a racing wheel that is 1% more efficient for 100% extra work. In which case, reimplement your string class and XML parser while you're at it!
Saturday 19 February 2011
Sunday 6 February 2011
Monday 31 January 2011
RGB/HSV in HLSL 2
Petr Kobalíček has spurred me on to look more closely at the the HLSL conversion between RGB and HSV colour spaces. With a bit of refactoring, I've managed to shave a few more GPU cycles off:
float3 RGBtoHSV(in float3 RGB)
{
float3 HSV = 0;
#if NO_ASM
HSV.z = max(RGB.r, max(RGB.g, RGB.b));
float M = min(RGB.r, min(RGB.g, RGB.b));
float C = HSV.z - M;
#else
float4 RGB4 = RGB.rgbr;
asm { max4 HSV.z, RGB4 };
asm { max4 RGB4.w, -RGB4 };
float C = HSV.z + RGB4.w;
#endif
if (C != 0)
{
float RGB0 = float4(RGB, 0);
float4 Delta = (HSV.z - RGB0) / C;
Delta.rgb -= Delta.brg;
Delta.rgb += float3(2,4,6);
Delta.rgb *= step(HSV.z, RGB.gbr);
#if NO_ASM
HSV.x = max(Delta.r, max(Delta.g, Delta.b));
#else
float4 Delta4 = Delta.rgbr;
asm { max4 HSV.x, Delta4 };
#endif
HSV.x = frac(HSV.x / 6);
HSV.y = 1 / Delta.w;
}
return HSV;
}
float3 RGBtoHSV(in float3 RGB)
{
float3 HSV = 0;
#if NO_ASM
HSV.z = max(RGB.r, max(RGB.g, RGB.b));
float M = min(RGB.r, min(RGB.g, RGB.b));
float C = HSV.z - M;
#else
float4 RGB4 = RGB.rgbr;
asm { max4 HSV.z, RGB4 };
asm { max4 RGB4.w, -RGB4 };
float C = HSV.z + RGB4.w;
#endif
if (C != 0)
{
float RGB0 = float4(RGB, 0);
float4 Delta = (HSV.z - RGB0) / C;
Delta.rgb -= Delta.brg;
Delta.rgb += float3(2,4,6);
Delta.rgb *= step(HSV.z, RGB.gbr);
#if NO_ASM
HSV.x = max(Delta.r, max(Delta.g, Delta.b));
#else
float4 Delta4 = Delta.rgbr;
asm { max4 HSV.x, Delta4 };
#endif
HSV.x = frac(HSV.x / 6);
HSV.y = 1 / Delta.w;
}
return HSV;
}
Sunday 30 January 2011
16-bit Shifts on Z80
One would think that 16-bit, unsigned, binary shifts on a Z80 microprocessor would be as trivial as it comes. But I've only recently realised just how little has been actually written down about optimising Z80 code for even these simplest of cases. So here's a quick guide; alas, probably thirty years too late!
The reason these shifts aren't obvious is because of the inherent asymmetry in the Z80 processor. Although it has a fairly orthogonal 8-bit ALU, its 16-bit (address) pipeline only has a simple adder. Multiplying HL by two (a left shift) is trivial: ADD HL, HL. Shifting right is a whole can of worms; to shift a 16-bit quantity to the right, it is sometimes quicker (and/or shorter) to shift/rotate left and adjust.
I've written a library of routines to perform the shifts, including some that use self-modifying code to eke out a few more T-cycles. I believe they're optimal. Anyone out there know different?
The reason these shifts aren't obvious is because of the inherent asymmetry in the Z80 processor. Although it has a fairly orthogonal 8-bit ALU, its 16-bit (address) pipeline only has a simple adder. Multiplying HL by two (a left shift) is trivial: ADD HL, HL. Shifting right is a whole can of worms; to shift a 16-bit quantity to the right, it is sometimes quicker (and/or shorter) to shift/rotate left and adjust.
I've written a library of routines to perform the shifts, including some that use self-modifying code to eke out a few more T-cycles. I believe they're optimal. Anyone out there know different?
Subscribe to:
Posts (Atom)