One of the most interesting parts of this code to me was the switch statement in the main loop. Professionally, I have run into programs that have massive switch statements with hundreds of cases in them, which I've always considered a "code smell". Your approach is interesting -- breaking everything up into logical groups, putting them in separate files, then using the preprocessor to put everything back together -- that's the first time I've seen that.
I'm still not sure how I feel about it -- you could argue that you have the same problem, but now it's spread out over many files. At the same time, I can look at the loop and have a pretty good idea of what it does and I can easily find what I'm looking for. I guess without OO, there isn't a great solution to the problem, although yours is probably a slight improvement, with the added bonus of getting me to think about the preprocessor in a new way.
IMHO there's no much simpler way to write a CPU emulator than as a huge switch inside a loop as that models quite well what a real CPU does. Anything more just obfuscates the essence of the functionality.
I suppose you could create a dictionary of opcode -> function pointers. In C# that'd be like
var ops = new Dictionary<Opcode, Action<opcodeArgs>> ()
{
{0, nop },
{1, add },
...
}
or even better, maybe I'd get a list of the opcodes with their names, name all the functions after those names exactly, then use reflection to automatically create this dictionary.
Then in the main loop,
ops[opcode].Invoke(args);
I don't know if something like that would be any use in C, though.
It's possible to do something like that in C. In C++, you can even do some template magic to create all the appropriate functions for each opcode based on something like the original instruction decode ROM. Brilliant and short code, but very difficult to write in the first place. Here's a very apropos example of this technique as applied to the 6502.
The problem with that method is that it's inefficient; with the switch statement, there's no hashing or significant computation involved in finding the location to jump to. With an object-based method, you have (at least) a memory access to get the method object, a lookup in a vtable to find the location of the Invoke method and a function call (which means pushing a new stack frame) before you start processing the instruction. With the switch statement, none of that happens; instead, it compiles to (essentially) two jump instructions; one into a lookup table, then one from the lookup table to the opcode handler. Getting out of the handler and back into the loop is also a single constant jump (no stack frame popping).
These are mostly O(1) things and shouldn't matter. The dictionary lookup can become O(1) if you turn it into an array of function pointers indexed by ints, the opcodes.
Yup, that's definitely true, but opcode parsing and dispatch needs to be a really tight loop. Keep in mind that function calls still involve pushing/popping stack frames, which while super, super cheap, are still not free. There are no loops other than the main loop in here, and every instruction takes constant time, which means that constant factors in opcode parsing and dispatch are going to really dominate.
In these kinds of situations, O(1) means nothing; any (reasonable) approach will be O(1), but constant factors will dominate.
For example, there are measurable differences in branch prediction based on whether each opcode's implementation ends with its own dispatch code or branches back to a common dispatch routine. The sequence of instructions is basically the same in both cases, but there is an icache/branch prediction tradeoff to be made. Subtle stuff (see http://repo.or.cz/w/luajit-2.0.git/blob/0ded8e82a88fadb40b4d...)
No dictionaries are O(1). Even if they were they are pitifully slow compared to a switch. Consider chaining and the fact that you have to compute hashes.
switch is O(1) and fast as the operation is usually 2-3 deterministic time instructions:
1. Look up jump address from lookup table made by compiler.
2. Jump to it.
More people need to have written assembly to actually get this into their heads.
In the case that the value you're switching on is a byte, and all 256 values are present in the case (very common for an 8-bit CPU emulator), a switch is a single instruction. You can't get faster/simpler than that.
Not to nitpick, but doesn't it need to be two instructions? You first do a relative jump by the value you're switching on, which takes you to another jump that jumps you to the actual code for that case. I can't think of a way to do it in just one instruction.
What are the common alternatives to avoid function calls when you want to wrap them? For example you want to wrap an adding function in order to have several cases for it (different architectures for example) but you also want to avoid wrapping it in a function call either via function pointer or switch to invoke different functions.
Macros can satisfy that in simple cases (like the NEXT_BYTE macro in x6502), and inline functions (like set_flags, get_flag, mem_abs, etc.) can handle more complex cases. Both of these result in the code in the contents of the "function" being directly inserted into the calling block. (For inline functions, that's only true if you turn on optimizations, but I think even O1 on any C99 compiler turns on inlining).
In general, if I need something to avoid function calls, I'll use macros as a shorthand for a complex expression (like NEXT_BYTE) and inline functions for anything that involves one or more full statements (like set_flags). Obviously I don't follow that too dogmatically, because mem_abs is an inline function.
At a previous job, there was a legacy UI application that had a 10,000+ line file called "messloop.c". It consisted of a function called messloop(), which was an infinite loop with a massive switch statement inside of it. I never had to work on it, but I'm told it was a maintenance nightmare.
So when I read the author's code and saw the way he handled a similar problem, I was intrigued. If you had to deal with a massive switch statement with thousands of cases, it would probably be easier to maintain IMHO.
For a hobby project, the author's solution is 100% correct and I'm not critiquing it at all. In fact, the only reason I even commented was because I thought his solution was better than any other procedural solution I had seen before.
With that said, in an enterprise environment, where maintainability is crucial, I'd argue that a massive switch statement is probably a bad idea. Going back to messloop.c, what happens if the user tries to change a floating point value through the UI? Well, I can tell you that there is a case statement for that somewhere in messloop.c. What is that case statement called? I'm not sure, all I know is it's a #define that I'm sure made sense to the original author. It's basically a needle-in-a-haystack problem.
The problem in your case (no pun intended) seems to be the difficulty of "finding the right branch". With a CPU emulator, it's not so hard: look up the opcode and there it is. Would you rather scan through a single file or several dozen?
That's what the debugger is for (if you inherited it).
However, a lot of desktop applications are written that way. If you've ever dealt with Win32, you'll see nested switch statements from hell on your average project.
If you have a pure OO language like Java or C# then there's no excuse but some legacy applications built in C tend to be "switchy" because the older APIs seem to favour that form of message dispatch.
There is still no excuse as you can have decent abstraction in C or C++.
However for what is effectively a jump table, a switch statement is exactly spot on for this project.
> Professionally, I have run into programs that have massive switch statements with hundreds of cases in them, which I've always considered a "code smell".
Most optimizing compilers will emit a jump table for a switch, which is a good way to get a reasonably high performance interpreter while keeping the code pretty clean. The performance is better than using virtual methods [1].
It's still a code smell. It might be a justifiable code smell in some very specific circumstances where the performance actually matters, but a code smell nevertheless.
You are substituting dogma for critical thinking. Sometimes a switch statement (or goto, etc.) is the best overall solution to a problem. In those cases, it is actually good code, not "justifiable code smell."
"Code smell" is just a heuristic. It's what happens when you look at some code and something in your head says "this doesn't look good". We all know that in the real world maintainability is more important than minor theoretical performance gains (yes, premature optimization without actually ever measuring the gains is a common thing, as we all hopefully know). I certainly didn't mean to imply that a switch or a goto statement cannot be the best way to implement something; I simply meant that a theoretical performance argument is not enough to make good code out of bad code.
A large switch statement is exactly the correct solution for this task. Other virtual machines do the same thing.
For example, look at Python's ceval.c, which has a switch table implementation and the text "The traditional bytecode evaluation loop uses a "switch" statement, which decent compilers will optimize as a single indirect branch instruction combined with a lookup table of jump addresses."
Actually, it also has a version which uses GCC's "Labels as Values" extension, see http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html . The comment then points out that this non-portable solution is up to 15-20% faster than a large switch statement, because having N jump points instead of 1 improves the CPU's branch prediction.
As haberman says, this is "actually good code", not "justifiable" bad code.
I am another who dislikes the term "code smell." I agree with mbrock - I would rather get a comment which reflects the underlying complaint than use a proxy term like "code smell". In your reply to mbrock you wrote:
> the "smell" is just a heuristic that says that without any additional arguments in its favor, a hundred-case switch usually isn't the cleanest and most maintainable way to implement something.
Why not just say "a hundred-case switch statement usually isn't ...", and omit a reference to "smell"? What additional meaning does the term "code smell" lend?
I dislike using the term "code smell" in general. Some people detest stinky cheeses, or a peaty whisky, while others adore the complex aromas. Often this is a learned taste which comes with age and experience.
As a result, the obvious rebuke to any "this is a code smell" comment is "that's because you aren't mature enough to appreciate it." I can't think of any effective response which stays on topic, other than to bring the conversation back to the specific problems in the code. Why not just start from that point instead?
I didn't even say anything about performance here. Even ignoring performance, sometimes switch() is best.
For example, I wrote a very small JIT compiler for Brainfuck. It uses switch() to do code generation. It's extremely clean and elegant code, if I do say so myself. Breaking apart the switch() with some kind of OO approach would make the program longer and less clear: http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-s...
Do you look at that and think "this doesn't look good" just because it uses switch()? If so, your heuristic could use a little tuning.
But now let's talk performance. In interpreter/VM main loops, bytecode dispatch is a huge factor in the overall performance of a VM. This issue isn't "theoretical," "minor," or "premature," it's the result of extensive measurement and experimentation by many people over a large amount of time. If you are not familiar with this well-studied issue, here is some recommended reading:
You have often a (single) big switch in lexers (often automatically generated). In network communication, you have often a big switch on the type of message (less often automatically generated).
It is not a bad pattern to have switch in those cases, but it is often present in code analyse rules. Code analyse tools are very bad to distinguish between good use of a feature that is rarely used and missuse of this feature.
Why not point out concrete problems with the implementation? We can't stop at the "smells" level of discussion! So, for example, is it hard to understand? Does it perform poorly? Is it difficult to extend?
It depends on the context. The context may make the smell go away; the "smell" is just a heuristic that says that without any additional arguments in its favor, a hundred-case switch usually isn't the cleanest and most maintainable way to implement something.
A smell is an indicator that something might be wrong, not that it is a sub-optimal way of doing things.
If you create code that smells, you should justify it (in comments or other documentation). Likewise just saying it smells without suggesting an alternative is unhelpful to the discussion.
Thanks! The one downside is that the local variables that are in scope (and heavily used) in the opcode handlers are all in a different file, which is super weird, but. On the other hand, I get the performance advantage of the switch statement jump table and, like you said, it's a lot easier to find whatever I'm looking for. I think it turned out a net win.
Even with OO, what would you suggest? A bunch of classes, classes, inheritance, and virtual methods to replace it? Great, now my logic is spread out all over the place.
I know that there are definitely cases in which an OO approach beats a bunch of switch statements scattered throughout the code (specifically, when checking the type of the input variable), but I don't think that applies here. If I saw an interpreter written in that way I would assume it was created by someone who didn't know what they were doing.
As an aside, 1) I can't stand the term "code smell" as it tends to be used by hipsters with little to no experience building complex systems, and 2) I realize that you posted your honest thoughts and did a good job of analyzing the approach taken by the author. I'm not trying to prop up a straw man here.
I just wanted to say that 'code smell' predates hipsters as a term, yo, and a lot of very experience engineers are right when they say 'this code smells' (i.e. I am suspicious of its suitability for consumption..) In my experience the term is something I learned in the 70's from the smelly retiring hippies whose code I had to maintain for a decade or so. ;)
See also http://c2.com/cgi/wiki?CodeSmell , which claims "A code smell is a hint that something has gone wrong somewhere in your code. ... KentBeck ... seems to have coined the phrase in the "OnceAndOnlyOnce" page" and points to "Refactoring" as earliest known use.
The phrase you refer to - "this code smells" - and the implication that it reflects hygiene, has different meaning and intent. While related, they are not the same thing as a "code "smell." Compare "the wine smells" vs. "wine smell" to get a sense of how there can be a difference.
fit2rule didn't say that the term 'code smell' outdates the term 'hipster'. He said that the term 'code smell' outdates hipsters' use of it. Heck, even I remember hearing about a code smell or two in the 80's ..
Ahh, I see how you can draw that conclusion. The quote is "'code smell' predates hipsters as a term", which I interpreted to mean that "code smell" (as a term) is older than "hipster" (as a term, or as a movement), while you interpret to mean "was used by people before hipsters used it."
I agree with you that "code smell" did not originate from hipster use.
But I don't think EpicEng implied that either. The parent comment is "it tends to be used by hipsters with little to no experience building complex systems." I don't see in it an implication that hipsters came up with the term.
So if that is fit2rule's point, then it's rather like saying "fixed-wheel bicycles existed before hipsters" - true, but a tangential remark which ended up derailing the conversation.
I know of no use of the phrase "code smell" before 1999, with its modern use. Could you speak more about your recollections from the 1980s? Was it used the same? Do you have a citation or reference?
Yeah, pretty much, and I admit it is a gross generalization. The term just bugs me because I see it used so often by people who lack the experience to understand why code which may not seem optimal to them was written the way that it was. I've also seen it used by smart people who know what they're talking about.
Who knew I would get such an education on the term "code smell"? :)
Which do you find easier to read/understand/work on? In my case, I still think the #include'd switch case's to be less readable in comparison .. although it must be admitted that oriculator uses the C macro processor in its own nefarious ways .. "READ_ZIX;" indeed .. ;)
(BTW, oriculator is a very mature 6502 emulator .. and also rocks as a way to return to the glory days of the 80's machines that never got enough love: the Oric-1 and Atmos...)
oriculator is much more powerful and complex than x6502, even you ignore the non-CPU emulation bits (like video and emulated controller, which are complex and difficult). The fact that they manage to get approximately correct timing is really impressive, and involves lots more logic around opcode decoding because you need tables of how many instructions each one takes.
That said, I find it much more difficult to read. The reliance on long, complex macros means that I need to flip back and forth between the macro definitions and the opcode interpreter, and long, multi-line macros like they have give me the heebie-jeebies. I'd rather see inline functions used (like x6502 does in functions.h; the functions in functions.h serve much the same purpose as the macros in oriculator).
Not to take anything away from the project -- it's obviously an amazing software project and an incredible achievement. We just set out with different goals in mind: mine was more pedagogical, and theirs was more practical.
Depending on the CPU, the compiler, and a whole host of other variables, a switch statement can be one of the better performing ways to write a virtual machine. You certainly want to avoid the overhead of many function calls.
And if you're writing a program that does one of hundreds of things based on the value of a byte, the options for really clean code are limited, and you can do a lot worse than a huge switch statement.
"The possibilities when compiling a switch are much more varied. It can result in a trivial series of if..else statements. It can result in a binary search. Or, if the values are consecutive, a jump table. Or for a complex sequence, some combination of these techniques. If each case simply assigns a different value to the same variable, then it can be implemented as a range check and array lookup. The overall sweep of the solutions, from hundreds of sequential, mispredicted comparisons to a single memory read, is substantial."
6502 is still currently used, by the way. The Tamagotchi games use a GeneralPlus 6502C, for example. There's a new Tamagotchi out this week that has NFC but is so much like the previous version otherwise, it's probably still using the same code:
The 6502 is still used widely in industrial stuff. Back in 2001 when I was still doing electrical engineering bits, as part of a project I received some brand new custom radio rig test equipment to integrate with an automated testing rig plugged into a PC running DOS (meh). We took it to bits to get access to the documented serial port header. Inside was a pristine gold turned pin socketed 6502. It was the only chip on the board that had that privilege. It was like it was being worshipped by the engineers.
We sent the test team who built it a big "you're awesome" card.
Good job! As promised, the code is easy to read. Your major choices all make sense to me:
* you went easy on #define's, they obfuscate the code when used heavily
* you have a simple switch instead of an opcode table with function pointers or something like that (may seem more fun to write, harder to read for someone inexperienced)
* light on comments, which is also excellent, the code is self-documenting almost everywhere.
* the unusual includes for opcode categories are OK. If you wanted it to be more straight-laced C, could put them in separate .c files and break the switch into many switches, one per file. It's probably better the way it is.
One thing it'd be nice to add is a short guide, right there in README, in which order to read the source (e.g. start from cpu.h/cc to understand how the state of the CPU and memory is represented, then cpu.h/cc for the main loop executed once per instruction, etc.).
The interesting thing is that I wrote very little of the code by hand; instead I wrote a program that generates the opcode implementations from a concise specification. The end result is remarkably compact, and the whole thing was really fun to make.
Thanks for the reference! This was a long time ago but I think I didn't use that decoding table. Just a couple of days ago I was reading about the x86 one, I really wasn't aware that opcodes were anything but random bytes mapped to operations.
As for the improvement, I never liked switch, for unknown reasons :) You mean an improvement in style, performance or both?
There's always a pattern to the opcodes, even though in some cases it's not quite so obvious (like x86 - it's octal too). The reason has to do with how it's implemented in hardware. The 6502's is only slightly less regular than the z80 and here's an interesting article about that: http://www.pagetable.com/?p=39
IMHO the switch would improve both style and performance, what caught my eye when I looked over the code was that different flags would require different amounts of time to execute, which really seems an odd thing for an emulator to do.
Oh that's an interesting idea. I actually considered writing my own assembler to some format that I could read, but then I realized:
* It's way cooler if you can use other peoples' assemblers and still have your bytecode interpreter work
* Using other peoples' assemblers is a good way to sanity check any assumptions you made (this bit me with branches, for example; I was branching from the location of the current instruction when the spec is to branch from the location of the next instruction)
* It's easier to read binary than to read hex strings and transform them to binary.
Plus, transforming binaries to readable hex is as easy as "hexdump -c". :)
If you want to try this out without building it yourself, I've put together a quick Emscripten port, including the two demos:
http://jamesfriend.com.au/x6502
71 comments
[ 3.0 ms ] story [ 160 ms ] threadI'm still not sure how I feel about it -- you could argue that you have the same problem, but now it's spread out over many files. At the same time, I can look at the loop and have a pretty good idea of what it does and I can easily find what I'm looking for. I guess without OO, there isn't a great solution to the problem, although yours is probably a slight improvement, with the added bonus of getting me to think about the preprocessor in a new way.
Good work.
Then in the main loop,
I don't know if something like that would be any use in C, though.http://www.youtube.com/watch?v=y71lli8MS8s
For example, there are measurable differences in branch prediction based on whether each opcode's implementation ends with its own dispatch code or branches back to a common dispatch routine. The sequence of instructions is basically the same in both cases, but there is an icache/branch prediction tradeoff to be made. Subtle stuff (see http://repo.or.cz/w/luajit-2.0.git/blob/0ded8e82a88fadb40b4d...)
I often hear this from programmers coming from a higher-level background. O(1) only means constant time. It doesn't mean quickly. This function:
is O(1), just as this: Foo takes an hour to commplete, but it's just as O(1) as bar :-).switch is O(1) and fast as the operation is usually 2-3 deterministic time instructions:
1. Look up jump address from lookup table made by compiler.
2. Jump to it.
More people need to have written assembly to actually get this into their heads.
In general, if I need something to avoid function calls, I'll use macros as a shorthand for a complex expression (like NEXT_BYTE) and inline functions for anything that involves one or more full statements (like set_flags). Obviously I don't follow that too dogmatically, because mem_abs is an inline function.
So when I read the author's code and saw the way he handled a similar problem, I was intrigued. If you had to deal with a massive switch statement with thousands of cases, it would probably be easier to maintain IMHO.
For a hobby project, the author's solution is 100% correct and I'm not critiquing it at all. In fact, the only reason I even commented was because I thought his solution was better than any other procedural solution I had seen before.
With that said, in an enterprise environment, where maintainability is crucial, I'd argue that a massive switch statement is probably a bad idea. Going back to messloop.c, what happens if the user tries to change a floating point value through the UI? Well, I can tell you that there is a case statement for that somewhere in messloop.c. What is that case statement called? I'm not sure, all I know is it's a #define that I'm sure made sense to the original author. It's basically a needle-in-a-haystack problem.
However, a lot of desktop applications are written that way. If you've ever dealt with Win32, you'll see nested switch statements from hell on your average project.
If you have a pure OO language like Java or C# then there's no excuse but some legacy applications built in C tend to be "switchy" because the older APIs seem to favour that form of message dispatch.
There is still no excuse as you can have decent abstraction in C or C++.
However for what is effectively a jump table, a switch statement is exactly spot on for this project.
Most optimizing compilers will emit a jump table for a switch, which is a good way to get a reasonably high performance interpreter while keeping the code pretty clean. The performance is better than using virtual methods [1].
[1]: http://www.complang.tuwien.ac.at/forth/threading/
For example, look at Python's ceval.c, which has a switch table implementation and the text "The traditional bytecode evaluation loop uses a "switch" statement, which decent compilers will optimize as a single indirect branch instruction combined with a lookup table of jump addresses."
Actually, it also has a version which uses GCC's "Labels as Values" extension, see http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html . The comment then points out that this non-portable solution is up to 15-20% faster than a large switch statement, because having N jump points instead of 1 improves the CPU's branch prediction.
As haberman says, this is "actually good code", not "justifiable" bad code.
I am another who dislikes the term "code smell." I agree with mbrock - I would rather get a comment which reflects the underlying complaint than use a proxy term like "code smell". In your reply to mbrock you wrote:
> the "smell" is just a heuristic that says that without any additional arguments in its favor, a hundred-case switch usually isn't the cleanest and most maintainable way to implement something.
Why not just say "a hundred-case switch statement usually isn't ...", and omit a reference to "smell"? What additional meaning does the term "code smell" lend?
I dislike using the term "code smell" in general. Some people detest stinky cheeses, or a peaty whisky, while others adore the complex aromas. Often this is a learned taste which comes with age and experience.
As a result, the obvious rebuke to any "this is a code smell" comment is "that's because you aren't mature enough to appreciate it." I can't think of any effective response which stays on topic, other than to bring the conversation back to the specific problems in the code. Why not just start from that point instead?
For example, I wrote a very small JIT compiler for Brainfuck. It uses switch() to do code generation. It's extremely clean and elegant code, if I do say so myself. Breaking apart the switch() with some kind of OO approach would make the program longer and less clear: http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-s...
Do you look at that and think "this doesn't look good" just because it uses switch()? If so, your heuristic could use a little tuning.
But now let's talk performance. In interpreter/VM main loops, bytecode dispatch is a huge factor in the overall performance of a VM. This issue isn't "theoretical," "minor," or "premature," it's the result of extensive measurement and experimentation by many people over a large amount of time. If you are not familiar with this well-studied issue, here is some recommended reading:
http://software.intel.com/en-us/forums/topic/298506
http://lua-users.org/lists/lua-l/2011-02/msg00742.html
http://www.cs.tcd.ie/publications/tech-reports/reports.07/TC... (see section 3.3)
It is not a bad pattern to have switch in those cases, but it is often present in code analyse rules. Code analyse tools are very bad to distinguish between good use of a feature that is rarely used and missuse of this feature.
Can you contextualise the criteria? Can you match those criteria to this context?
(Not rhetorical, I'd like to know the answer.)
If you create code that smells, you should justify it (in comments or other documentation). Likewise just saying it smells without suggesting an alternative is unhelpful to the discussion.
But people do use them an awful lot for watching movies too...
I know that there are definitely cases in which an OO approach beats a bunch of switch statements scattered throughout the code (specifically, when checking the type of the input variable), but I don't think that applies here. If I saw an interpreter written in that way I would assume it was created by someone who didn't know what they were doing.
As an aside, 1) I can't stand the term "code smell" as it tends to be used by hipsters with little to no experience building complex systems, and 2) I realize that you posted your honest thoughts and did a good job of analyzing the approach taken by the author. I'm not trying to prop up a straw man here.
http://smalltalkhub.com/#!/~zeroflag/NesTalk
To get a sense of its history, https://books.google.com/ngrams/graph?content=code+smell&yea... put a start date of around 1998. It was definitely in Fowler (1999) "Refactoring: Improving the Design of Existing Code".
See also http://c2.com/cgi/wiki?CodeSmell , which claims "A code smell is a hint that something has gone wrong somewhere in your code. ... KentBeck ... seems to have coined the phrase in the "OnceAndOnlyOnce" page" and points to "Refactoring" as earliest known use.
The phrase you refer to - "this code smells" - and the implication that it reflects hygiene, has different meaning and intent. While related, they are not the same thing as a "code "smell." Compare "the wine smells" vs. "wine smell" to get a sense of how there can be a difference.
The modern use of the term "hipster" dates from 1999-2003, claims Wikipedia at http://en.wikipedia.org/wiki/Hipster_%28contemporary_subcult... , but it's easy to find things like the 1998 alt-comic "Urban Hipster #1" at http://www.indyworld.com/uh/uh01.html which predate 1999 and use hipster in its modern meaning.
In any case, the term 'hipster' is derived from a term in widespread use from the 40s-60s, which then fell into disfavor. To get a rough idea of the change in use pattern, see https://books.google.com/ngrams/graph?content=hipster&year_s... .
Obviously the 1940s "hipster" term predates "code smell" as applied to computer code, because there was no computer code in the 1940s.
So no, "code smell" does not predate the term "hipster."
I agree with you that "code smell" did not originate from hipster use.
But I don't think EpicEng implied that either. The parent comment is "it tends to be used by hipsters with little to no experience building complex systems." I don't see in it an implication that hipsters came up with the term.
So if that is fit2rule's point, then it's rather like saying "fixed-wheel bicycles existed before hipsters" - true, but a tangential remark which ended up derailing the conversation.
I know of no use of the phrase "code smell" before 1999, with its modern use. Could you speak more about your recollections from the 1980s? Was it used the same? Do you have a citation or reference?
Who knew I would get such an education on the term "code smell"? :)
https://code.google.com/p/oriculator/source/browse/trunk/650...
Which do you find easier to read/understand/work on? In my case, I still think the #include'd switch case's to be less readable in comparison .. although it must be admitted that oriculator uses the C macro processor in its own nefarious ways .. "READ_ZIX;" indeed .. ;)
(BTW, oriculator is a very mature 6502 emulator .. and also rocks as a way to return to the glory days of the 80's machines that never got enough love: the Oric-1 and Atmos...)
That said, I find it much more difficult to read. The reliance on long, complex macros means that I need to flip back and forth between the macro definitions and the opcode interpreter, and long, multi-line macros like they have give me the heebie-jeebies. I'd rather see inline functions used (like x6502 does in functions.h; the functions in functions.h serve much the same purpose as the macros in oriculator).
Not to take anything away from the project -- it's obviously an amazing software project and an incredible achievement. We just set out with different goals in mind: mine was more pedagogical, and theirs was more practical.
And if you're writing a program that does one of hundreds of things based on the value of a byte, the options for really clean code are limited, and you can do a lot worse than a huge switch statement.
Here is an interesting article on the topic:
http://www.complang.tuwien.ac.at/forth/threading/
http://prog21.dadgum.com/166.html
"The possibilities when compiling a switch are much more varied. It can result in a trivial series of if..else statements. It can result in a binary search. Or, if the values are consecutive, a jump table. Or for a complex sequence, some combination of these techniques. If each case simply assigns a different value to the same variable, then it can be implemented as a range check and array lookup. The overall sweep of the solutions, from hundreds of sequential, mispredicted comparisons to a single memory read, is substantial."
http://natashenka.ca/tamagotchi-friends-teardown/
We sent the test team who built it a big "you're awesome" card.
* you went easy on #define's, they obfuscate the code when used heavily
* you have a simple switch instead of an opcode table with function pointers or something like that (may seem more fun to write, harder to read for someone inexperienced)
* light on comments, which is also excellent, the code is self-documenting almost everywhere.
* the unusual includes for opcode categories are OK. If you wanted it to be more straight-laced C, could put them in separate .c files and break the switch into many switches, one per file. It's probably better the way it is.
One thing it'd be nice to add is a short guide, right there in README, in which order to read the source (e.g. start from cpu.h/cc to understand how the state of the CPU and memory is represented, then cpu.h/cc for the main loop executed once per instruction, etc.).
http://simulationcorner.net/index.php?page=c64
Only 30KB of code and it runs a C64 on it.
The interesting thing is that I wrote very little of the code by hand; instead I wrote a program that generates the opcode implementations from a concise specification. The end result is remarkably compact, and the whole thing was really fun to make.
Did you use this for generating the opcodes? The Z80 ISA encoding is quite regular and octal-based: http://www.z80.info/decoding.htm
As for the improvement, I never liked switch, for unknown reasons :) You mean an improvement in style, performance or both?
IMHO the switch would improve both style and performance, what caught my eye when I looked over the code was that different flags would require different amounts of time to execute, which really seems an odd thing for an emulator to do.
He suggested to take HEX as the input and I never looked back. I see this emulator just reads the bytes from the binary.
Does anyone know if the GCC AVR toolchain spits out a file I could do the same thing with?
* It's way cooler if you can use other peoples' assemblers and still have your bytecode interpreter work
* Using other peoples' assemblers is a good way to sanity check any assumptions you made (this bit me with branches, for example; I was branching from the location of the current instruction when the spec is to branch from the location of the next instruction)
* It's easier to read binary than to read hex strings and transform them to binary.
Plus, transforming binaries to readable hex is as easy as "hexdump -c". :)
How about inline functions or macros?