The best part of this article is in the comments. An original programmer on the game explains the reason that the bug was occurring and also why the code was unreadable (hint: they did it in assembly)
Ah, the quantum tunneling pinball!
We ran into this while writing the original code at Cinematronics in 1994. Since the ball motion, physics, and coordinates were all in floating point, and the ball is constantly being pushed "down" the sloped table by the gravity vector in every frame, we found that floating point error would gradually accumulate until the ball's position was suddenly on the other side of the barrier!
(To simplify collision detection, the ball was reduced to a single point/vector and all barriers were inflated by the ball radius. So, if the mathematical point got too "close" to the mathematical line barrier, a tiny amount of floating point rounding or truncation error could push the point to the other side of the line)
To mitigate that, we added a tiny amount of extra bounce to push the ball away from the barrier when it was nearly at rest, to keep the floating point error accumulation at bay. This became known as the Brownian Motion solution.
Since much of the original code was written in x86 asm to hand tailor Pentium U/V pipelines and interleave FPU instructions, but wiki says Microsoft ported the code to C for non-Intel platforms, I'm sure the code had passed through many hands by the time it got to you. The Brownian Motion solution may have been refactored into oblivion.
I've had similiar problem in one of my first games (scrolling shooter with asteroid physic, hobby project in TP7 and mode 13h), and I solved it in similiar way (when objects bounced off the obstacle I added constant to response vector length, so that ship always bounced with velocity so big, that even with gravity added, and when player rotated towards the obstacle and engaged engine, it would still move out of the obstacle in next frame).
It looked a little funny, cause when you left the ship to itself, it bounced in a place to half its height and back forever.
To mitigate this I've added 2 kinds of obstacles - most of the terrain was filled with obstacles with spikes, that did damage on each collision, so players won't test my collision response too much :) There were some obstacles without spikes, and you could forever bounce on them, but they were rare, and I marked them as "landing places". I think that added to gameplay.
Then I've added buttons that trigger doors to open/close, and in a few levels I've just placed some ship with no ai attached over the button. Effect - ship bounced forever, so the doors connected to the button opened and closed repeatedly ;) Player could also influence that by pushing the triggering ship away :)
It's funny how constraints of game engine can guide you into gameplay elements that you wouldn't consider without them.
Maybe the reason the Brownian motion stopped working was a shift from 80-bit x87 intermediaries to 64-bit SSE in x64, and the removal of long double support from MSVC.
This is an excellent example of why comments in code are so very useful, helpful and in my opinion, necessary.
... nobody at Microsoft ever understood how the code worked (much less still understood it), and that most of the code was completely uncommented, we simply couldn't figure out why the collision detector was not working. Heck, we couldn't even find the collision detector!
To all those folks out there who keep finding reasons to try and be clever with their language, or omit comments from their code: now you can remember Pinball. It's very possible that with some minimal commenting about what functions did, or what the intentions of block of codes were, Pinball could have lived on. Heck, we could have even seen a Modern UI version of Pinball.
There are some readability problems that stem from a lack of comments, and there are other readability problems that stem from exceedingly poorly-written code.
Heck, we couldn't even find the collision detector!
This sentence tells me that the big problem here comes from the latter, much worse, source of of readability problems. The fact that they couldn't find the collision detector implies that the code had no class named CollisionDetector, no procedure named DetectCollisions, nothing of the sort. Which implies (if not conclusively) that there was a whole lot spaghetti code floating around in there. Or at least a whole lot of very poorly-factored code.
I do think that comments are an important part of making code readable and maintainable. But comments are there to handle less drastic readability concerns. Documenting what units a parameter should be in, for example, or saying, "The following code implements X algorithm with Y and Z optimizations applied." Comments should never just be a substitute for using language-level code organizational and structuring constructs to organize and structure one's code.
I'm pretty sure I saw a library somewhere that could be purchased in both "raw code" and "commented, documented code" forms.
Which I can only assume meant they had the comments/documents and were intentionally stripping them out for the cheaper version. The difference in price was also actually quite significant as well, as I recall.
That, along with the fact that the person doing the purchasing is not necessarily the person who would be using the product, makes this IMO a particularly nasty sales tactic.
I've also heard war stories from Big Company folks about contracting out for a large software project, and per contract receiving the full source for the app, but none of the (particularly convoluted) build scripts or Makefiles.
Lessons Learned are why BigCo has a 900 page specification document including glossary of what the word "means" means.
This is also a great example of what happens during acquisitions.
Space Cadet was developed by Cinematronics (apparently no connection to the classic arcade game manufacturer) and published by Maxis. Maxis eventually bought Cinematronics. Then (according to Wikipedia) "The studio was closed and the employees were laid off when Maxis was acquired by Electronic Arts in 1997."
So someone out there knows how to fix it. I bet Microsoft called EA looking for a quick/free fix, EA called the employee (or lost track of them, more likely), and the employee told them to stick it.
It's an argument for clear code that's readily understandable by other people. It's not an argument for any particular technique for achieving that goal. I've never seen any programmer seriously suggest that code should be deliberately made difficult to understand.
Comments under the article seem to imply that most of the relevant code was somehow hand converted from (probably well commented) assembly to C which I only assume was done by someone who had no interest in how or why the code actually works, which seems to perfectly explain the lack of comments (or meaningful structure for that matter).
I usually argue that version control history is often as useful as code comments. In this case, though, I suppose comments would have been better than some arcane 15 year old VCS :)
I can't try this right now, since I don't currently have access to a Windows machine, but can anyone confirm/deny this? Do you get the floating-point gravity bug?
Heh, 's nice to read about that and as a pinball-enthusiast i'm a bit saddened for it to be gone, even if it is more for retro-reasons as opposed to it being special in some way...
Maybe MS should team up with someone who understands making (digital) pinballs, like the guys that created Zen Pinball?
Look for Danny Thorpe's comments, he mentions that it was created by a company called "Cinematronics", which was later acquired by Maxis, which was then acquired by EA.
To me, the lesson is: Do not duplicate code! and keep it simple stupid
When implementing Unicode, MS said "hey let's copy all of our API functions to new names". Now they have two problems.
When implementing 64-bit, MS said "hey let's have a completely different OS for 64-bit." Now they have four problems.
Compare this to Unix/Linux/MacOS where Unicode is just implemented as a standard way (UTF-8) of encoding characters into the existing API. And there need only be one shipping OS which supports both 32-bit and 64-bit process types equally since there are only a handful of kernel APIs compared to thousands for Windows.
There is a great deal of both misunderstanding and ignorant Microsoft bashing in this comment.
First of all, you are mixing up two completely different concepts.
For character encoding on Windows:
For many functions in the Windows API there two versions of a function, one with an A (for ANSI) at the end and one with a W (for wide). This was added to make it easier to support Win32 on both Windows 95, which used 8-bit characters and codepages and Windows NT which was natively utf-16 unicode. At the time utf-16 was considered the best and most standard choice for supporting unicode. In most cases it is implemented as W function with an A function that is little more than a wrapper.
This has nothing to do with what Raymond is describing.
For the 64-32 bit stuff they ensured that all code would compile and work correctly with both 32/64 bit stuff and built two versions, one for ia32 and one for amd64. The kernel would have to be modified to support the amd64 architecture. This is exactly what Linux, OSX and other operating systems that support multiple architectures do. On top of this, because amd64 supports backwards compatibility, they also included an ia32 environment with it as well, but this is optional, so anything that ships with the OS cannot depend on it. I assume this is what OSX does too, the only difference is that with Windows the two versions ship as different SKAs, and MacOSX ships with both versions and installs the one that the computer originally shipped with.
Second, the number of system calls has nothing do with any of this at all.
Windows unicode support predates the existence of UTF-8 -- so that's great API design for Windows if you possess a time machine.
The ANSI functions merely map to the unicode functions.
In addition, the Windows Kernel is probably similar in API size to the Linux kernel. Of course, that's not nearly enough API for a complete Windowing operating system in either case.
What are the consequences of your preferred approach for legacy code? While there are certainly disadvantages of duplicating APIs in terms of cruft, I think that many customers appreciate that old code continues to run (and compile) against newer versions of the OS.
Apparently, someone has actually FIXED it and put it online. Although hard to know if the linked EXE isn't just a trojan. Anyone have a clean room they can test it in?
[That would have been even more work, because there was at the time no infrastructure in Setup for having 32-bit-only components. (And then automatically uninstalling it when WOW64 was disabled.) And besides, all the people who criticized Windows 96 as "not really a 32-bit operating system because it has some parts in 16-bit" would use the same logic to say that 64-bit Windows is "not really a 64-bit operating system." -Raymond]
Yup. All you've got to do is copy it over from a Windows XP machine, if you have one laying around.
Aside from the explanation others have quoted, I'm guessing some of it was also an aesthetic decision. They could probably still go and bring it back using WOW64, but it still looks like a Win95-era program, whereas all the other games have been replaced with better-looking rewrites. That and I think it only supports a resolution of 640x480.
> nobody at Microsoft ever understood how the code worked (much less still understood it), and that most of the code was completely uncommented, we simply couldn't figure out why the collision detector was not working. Heck, we couldn't even find the collision detector!
This continues to be one of my pet peeves, particularly with code samples and a lot of what is posted on Github, even major libraries. Almost no comments, background or guidance on the intent and structure or the code.
No, I am not suggesting that something like this is necessary:
// Iterate through all elements
for(int i=0; i < count; i++
{
// Check that velocity isn't above threshold
if(velocity[i] > THRESHOLD)
{
// Limit velocity
velocity[i] = THRESHOLD;
...
That's ridiculous. However, something like this is useful:
I've heard some say "I just write self-documenting code". That's a myth but for the simplest of structures. Any non-trivial piece of work is far from being self-documenting. Code is self-documenting for the guy who wrote it. I guarantee you that anyone else reading it has to reconstruct a stack in their head to understand what the hell is going on. That's not self-documentation. I shouldn't have to think to understand what a chunk-o-code is doing. The same for functions and/or methods.
The myth of self-documenting code is easy to demonstrate if I show you a piece of code in a language you don't know. I am going to assume that most programmers these days don't know assembler, Forth or Lisp.
I wrote this twenty years ago. Even if you understand Lisp you'd have to think it through. However, this is not how I wrote it. This is what I actually wrote:
;=====================================================================================================
; GetPolylineEndEntities
;
; Argument: Polyline entity name
; Return: Two element list containing the first (if any) entity found at the end of the polyline.
; The polyline itself is excluded.
; If nothing is found at a particular end, that element in the list is set to nil.
;
(defun GetPolylineEndEntities ( plename / plends cvcenter cvsize oldcmdecho pt1 pt2 endss outlist)
(setq plends (GetPolylineEnds plename)) ;Get the endpoints
(setq cvcenter (getvar "viewctr")
cvsize (getvar "viewsize")
oldcmdecho (getvar "CMDECHO")
)
(setvar "CMDECHO" 0)
(foreach point plends
(progn
;Examine what connects at each end
(setq pt1 (add2d point '(-0.0125 -0.0125)))
(setq pt2 (add2d pt1 '(0.025 0.025)))
;Zoom to the end being analyzed to have better selection accuracy
; **** Have to figure out a way to do this without zooming ****
(command "zoom" "c" point 2)
;Eliminate the original cable from the resulting selection set
(setq endss (ssdel plename (ssget "C" pt2 pt1)))
;Add the first entity found to the output list
(setq outlist (append outlist (list (ssname endss 0))))
)
)
(command "zoom" "c" cvcenter cvsize)
(setvar "CMDECHO" oldcmdecho)
outlist
)
Even if you don't know Lisp you now have an idea of what this code is doing. My style has changed over the years. This isn't my best example, but it is here to drive a point home.
The use of an unfamiliar language serves to illustrate the point that the idea of self-documenting code is, again, a myth. I wrote that code myself and without the comments I'd have to mentally reconstruct every step to even begin to understand what's going on and what the intent was. I haven't touched Lisp in quite some time.
I've looked through so much code in Github without a single comment that it makes me wonder if this is what is being taught in schools these days. Accurate in-code documentation is, as far as I am concerned, part and parcel of becoming a professional programmer. It recognizes that the work represents a huge investment in time, money and intellectual effort and it ensures that this effort and expense doesn't have to be duplicated in order to maintain, evolve or migrate the product as the Microsoft example clearly demonstrates.
MSG_LOOP_PostMessage:
mov a, MSG_Head
cjne a, #MSG_BUFFER_END - 2, mlpm0
mov a, #MSG_BUFFER
sjmp mlpm1
mlpm0:
inc a
inc a
mlpm1:
cjne a, MSG_Tail, mlpm2
clr a
ret
mlpm2:
mov r0, MSG_Head
mov @r0, MSG_Code
inc r0
mov @r0, MSG_Parameter
mov MSG_Head, a
mov a, #1
ret
I wrote that about fifteen years ago. Of course, the routine's label tells you something: "MSG_LOOP_PostMessage". It must post a message to a message buffer. The rest is giberish. What's the intent behind each and every block of code? Well, of course, that's not how I wrote it. This is what I wrote:
; POST MESSAGE --------------------------------------------------------------------------
; This routine posts a new message to the message loop buffer.
;
; The message code and parameter are written to the buffer only if buffer space is
; available. This is determined by first looking at the difference between the
; head and tail pointers. By design, we sacrifice one set of locations (two
; bytes) in the buffer in order to create a gap between an advancing head pointer and
; a lagging tail pointer. If a lot of messages are issued and not processed immediately
; the head pointer will quickly wrap around and threaten to collide with the tail
; pointer. By sacrificing a set of locations we avoid having to keep a counter of
; unprocessed messages. This, because there would be ambiguity when both head and
; tail pointers point to the same location: it could mean that there are no messages
; to process or that there's a buffer full of messages to process. The two byte
; gap we are imposing between head and tail removes this ambiguity and makes it easy
; to determine the buffer empty and full conditions.
;
; If there's space for a new message it is stored and the head pointer advanced to
; the next available location. However, if no space remains, the message is discarded
; and the head pointer will remain one message (two bytes) away from the tail pointer.
;
; Arguments
; ----------------------------
; MSG_Head Message buffer head pointer
; MSG_Tail Message buffer tail pointer
; MSG_Code Message code
; MSG_Parameter Message parameter
;
; Return
; ----------------------------
; ACC = 0 -> Message did not post
; ACC <> 0 -> Message posted
;
; Modified
; ----------------------------
; MSG_Head
; ACC, R0
;
; Preserved
; ----------------------------
; MSG_Tail
; MSG_Code
; MSG_Parameter
;
MSG_LOOP_PostMessage:
mov a, MSG_Head ;Increment the head pointer by one message
cjne a, #MSG_BUFFER_END - 2, mlpm0 ;and parameter.
mov a, #MSG_BUFFER ;Need to wrap around.
sjmp mlpm1 ;
mlpm0: ;
inc a ;No need to wrap around, just increment.
inc a ;
mlpm1:
cjne a, MSG_Tail, mlpm2 ;Check for a buffer full condition.
clr a ;Flag that we did not post the message.
ret ;Exit if it is.
mlpm2:
;The buffer isn't full, we can store the new message
mov r0, MSG_Head ;Store message
mov @r0, MSG_Code
inc r0
mov @r0, MSG_Parameter ;Store parameter
;Now set the head pointer to the next available message location.
mov MSG_Head, a ;The accumulator already has the next "head" location
;there's no need to execute the same logic again.
mov a, #1 ;Flag that the message was posted
ret ;and exit
Now you don't have to know assembler to understand this code. A couple of years later I had to re-write this in C. It was incredibly easy to do because of the exhaustive in-code documentation. This is what came out:
// POST MESSAGE --------------------------------------------------------------------------
// This routine posts a new message to the message loop buffer.
//
// The message code and parameter are written to the buffer only if buffer space is
// available. This is determined by first looking at the difference between the
// head and tail pointers. By design, we sacrifice one set of locations (two
// bytes) in the buffer in order to create a gap between an advancing head pointer and
// a lagging tail pointer. If a lot of messages are issued and not processed immediately
// the head pointer will quickly wrap around and threaten to collide with the tail
// pointer. By sacrificing a set of locations we avoid having to keep a counter of
// unprocessed messages. This, because there would be ambiguity when both head and
// tail pointers point to the same location: it could mean that there are no messages
// to process or that there's a buffer full of messages to process. The two byte
// gap we are imposing between head and tail removes this ambiguity and makes it easy
// to determine the buffer empty and full conditions.
//
// If there's space for a new message it is stored and the head pointer advanced to
// the next available location. However, if no space remains, the message is discarded
// and the head pointer will remain one message (two bytes) away from the tail pointer.
//
//
// Return
// ----------------------------
// 0 = Message did not post
// 1 = Message posted
//
//
U8 msg_loop_post_message(U8 message, U16 parameter)
{
if(msg_loop.count == MESSAGE_LOOP_SIZE)
{
return(0); // Can't post because buffer is full
}
else
{
msg_loop.item[msg_loop.head].message = message; // Post message
msg_loop.item[msg_loop.head].parameter = parameter; //
msg_loop.count++; // Update message count
if( msg_loop.head == (MESSAGE_LOOP_SIZE - 1)) // Wrap around?
{
msg_loop.head = 0; // Yes.
}
else
{
msg_loop.head++; // No
}
return(1);
}
}
I used to play Pinball at school whenever I was in the Computer Lab. I also spent the bulk of my time in the computer lab at recess. Brings back old memories.
[+] [-] shmageggy|13 years ago|reply
Ah, the quantum tunneling pinball!
We ran into this while writing the original code at Cinematronics in 1994. Since the ball motion, physics, and coordinates were all in floating point, and the ball is constantly being pushed "down" the sloped table by the gravity vector in every frame, we found that floating point error would gradually accumulate until the ball's position was suddenly on the other side of the barrier!
(To simplify collision detection, the ball was reduced to a single point/vector and all barriers were inflated by the ball radius. So, if the mathematical point got too "close" to the mathematical line barrier, a tiny amount of floating point rounding or truncation error could push the point to the other side of the line)
To mitigate that, we added a tiny amount of extra bounce to push the ball away from the barrier when it was nearly at rest, to keep the floating point error accumulation at bay. This became known as the Brownian Motion solution.
Since much of the original code was written in x86 asm to hand tailor Pentium U/V pipelines and interleave FPU instructions, but wiki says Microsoft ported the code to C for non-Intel platforms, I'm sure the code had passed through many hands by the time it got to you. The Brownian Motion solution may have been refactored into oblivion.
http://blogs.msdn.com/b/oldnewthing/archive/2012/12/18/10378...
[+] [-] ajuc|13 years ago|reply
It looked a little funny, cause when you left the ship to itself, it bounced in a place to half its height and back forever.
To mitigate this I've added 2 kinds of obstacles - most of the terrain was filled with obstacles with spikes, that did damage on each collision, so players won't test my collision response too much :) There were some obstacles without spikes, and you could forever bounce on them, but they were rare, and I marked them as "landing places". I think that added to gameplay.
Then I've added buttons that trigger doors to open/close, and in a few levels I've just placed some ship with no ai attached over the button. Effect - ship bounced forever, so the doors connected to the button opened and closed repeatedly ;) Player could also influence that by pushing the triggering ship away :)
It's funny how constraints of game engine can guide you into gameplay elements that you wouldn't consider without them.
EDIT: the game if anyone is interested: http://ajuc.ninja.umcs.pl/programy/gry/hw/download/hw.tar.gz it's in Polish, sorry, and coding style is outrageus
[+] [-] Evgeny|13 years ago|reply
[+] [-] bcoates|13 years ago|reply
[+] [-] rm999|13 years ago|reply
[+] [-] randomdrake|13 years ago|reply
... nobody at Microsoft ever understood how the code worked (much less still understood it), and that most of the code was completely uncommented, we simply couldn't figure out why the collision detector was not working. Heck, we couldn't even find the collision detector!
To all those folks out there who keep finding reasons to try and be clever with their language, or omit comments from their code: now you can remember Pinball. It's very possible that with some minimal commenting about what functions did, or what the intentions of block of codes were, Pinball could have lived on. Heck, we could have even seen a Modern UI version of Pinball.
[+] [-] bunderbunder|13 years ago|reply
Heck, we couldn't even find the collision detector!
This sentence tells me that the big problem here comes from the latter, much worse, source of of readability problems. The fact that they couldn't find the collision detector implies that the code had no class named CollisionDetector, no procedure named DetectCollisions, nothing of the sort. Which implies (if not conclusively) that there was a whole lot spaghetti code floating around in there. Or at least a whole lot of very poorly-factored code.
I do think that comments are an important part of making code readable and maintainable. But comments are there to handle less drastic readability concerns. Documenting what units a parameter should be in, for example, or saying, "The following code implements X algorithm with Y and Z optimizations applied." Comments should never just be a substitute for using language-level code organizational and structuring constructs to organize and structure one's code.
[+] [-] shabble|13 years ago|reply
Which I can only assume meant they had the comments/documents and were intentionally stripping them out for the cheaper version. The difference in price was also actually quite significant as well, as I recall.
That, along with the fact that the person doing the purchasing is not necessarily the person who would be using the product, makes this IMO a particularly nasty sales tactic.
I've also heard war stories from Big Company folks about contracting out for a large software project, and per contract receiving the full source for the app, but none of the (particularly convoluted) build scripts or Makefiles.
Lessons Learned are why BigCo has a 900 page specification document including glossary of what the word "means" means.
[+] [-] joezydeco|13 years ago|reply
Space Cadet was developed by Cinematronics (apparently no connection to the classic arcade game manufacturer) and published by Maxis. Maxis eventually bought Cinematronics. Then (according to Wikipedia) "The studio was closed and the employees were laid off when Maxis was acquired by Electronic Arts in 1997."
So someone out there knows how to fix it. I bet Microsoft called EA looking for a quick/free fix, EA called the employee (or lost track of them, more likely), and the employee told them to stick it.
[+] [-] mikeash|13 years ago|reply
[+] [-] MichaelApproved|13 years ago|reply
[+] [-] dfox|13 years ago|reply
[+] [-] TeMPOraL|13 years ago|reply
[+] [-] eps|13 years ago|reply
[+] [-] kamjam|13 years ago|reply
http://news.ycombinator.com/item?id=4934417
[+] [-] augustl|13 years ago|reply
[+] [-] streptomycin|13 years ago|reply
[+] [-] MatthewPhillips|13 years ago|reply
[+] [-] prezjordan|13 years ago|reply
I am surprised, however, that it hasn't been cloned yet.
[+] [-] LeonimuZ|13 years ago|reply
[+] [-] ww520|13 years ago|reply
[+] [-] crpatino|13 years ago|reply
[+] [-] barbs|13 years ago|reply
http://mspinball.weebly.com/
I can't try this right now, since I don't currently have access to a Windows machine, but can anyone confirm/deny this? Do you get the floating-point gravity bug?
[+] [-] shrikant|13 years ago|reply
[+] [-] moepstar|13 years ago|reply
Maybe MS should team up with someone who understands making (digital) pinballs, like the guys that created Zen Pinball?
[+] [-] tolos|13 years ago|reply
> The source code was licensed from another company. If you want the source code, you have to go ask them.
Twice this mystery company is referenced, but never a name is mentioned.
[+] [-] bunderbunder|13 years ago|reply
http://en.wikipedia.org/wiki/Full_Tilt!_Pinball
[+] [-] barbs|13 years ago|reply
[+] [-] cjensen|13 years ago|reply
When implementing Unicode, MS said "hey let's copy all of our API functions to new names". Now they have two problems.
When implementing 64-bit, MS said "hey let's have a completely different OS for 64-bit." Now they have four problems.
Compare this to Unix/Linux/MacOS where Unicode is just implemented as a standard way (UTF-8) of encoding characters into the existing API. And there need only be one shipping OS which supports both 32-bit and 64-bit process types equally since there are only a handful of kernel APIs compared to thousands for Windows.
[+] [-] correctifier|13 years ago|reply
First of all, you are mixing up two completely different concepts.
For character encoding on Windows: For many functions in the Windows API there two versions of a function, one with an A (for ANSI) at the end and one with a W (for wide). This was added to make it easier to support Win32 on both Windows 95, which used 8-bit characters and codepages and Windows NT which was natively utf-16 unicode. At the time utf-16 was considered the best and most standard choice for supporting unicode. In most cases it is implemented as W function with an A function that is little more than a wrapper.
This has nothing to do with what Raymond is describing.
For the 64-32 bit stuff they ensured that all code would compile and work correctly with both 32/64 bit stuff and built two versions, one for ia32 and one for amd64. The kernel would have to be modified to support the amd64 architecture. This is exactly what Linux, OSX and other operating systems that support multiple architectures do. On top of this, because amd64 supports backwards compatibility, they also included an ia32 environment with it as well, but this is optional, so anything that ships with the OS cannot depend on it. I assume this is what OSX does too, the only difference is that with Windows the two versions ship as different SKAs, and MacOSX ships with both versions and installs the one that the computer originally shipped with.
Second, the number of system calls has nothing do with any of this at all.
[+] [-] wvenable|13 years ago|reply
The ANSI functions merely map to the unicode functions.
In addition, the Windows Kernel is probably similar in API size to the Linux kernel. Of course, that's not nearly enough API for a complete Windowing operating system in either case.
[+] [-] kvb|13 years ago|reply
[+] [-] barrkel|13 years ago|reply
Which is MS's separate OS for 64-bit?
OS APIs are distinct from kernel APIs. You're comparing a kernel (Linux) with a distribution (Windows).
[+] [-] ww520|13 years ago|reply
[+] [-] zaroth|13 years ago|reply
http://mspinball.weebly.com/index.html
See: http://blogs.msdn.com/b/oldnewthing/archive/2012/12/18/10378...
[+] [-] crescentfresh|13 years ago|reply
[+] [-] missing_cipher|13 years ago|reply
[That would have been even more work, because there was at the time no infrastructure in Setup for having 32-bit-only components. (And then automatically uninstalling it when WOW64 was disabled.) And besides, all the people who criticized Windows 96 as "not really a 32-bit operating system because it has some parts in 16-bit" would use the same logic to say that 64-bit Windows is "not really a 64-bit operating system." -Raymond]
[+] [-] dmlorenzetti|13 years ago|reply
[+] [-] bunderbunder|13 years ago|reply
Aside from the explanation others have quoted, I'm guessing some of it was also an aesthetic decision. They could probably still go and bring it back using WOW64, but it still looks like a Win95-era program, whereas all the other games have been replaced with better-looking rewrites. That and I think it only supports a resolution of 640x480.
[+] [-] zspade|13 years ago|reply
[+] [-] robomartin|13 years ago|reply
This continues to be one of my pet peeves, particularly with code samples and a lot of what is posted on Github, even major libraries. Almost no comments, background or guidance on the intent and structure or the code.
No, I am not suggesting that something like this is necessary:
That's ridiculous. However, something like this is useful: Anyhow, dumb example I pulled out of thin air.I've heard some say "I just write self-documenting code". That's a myth but for the simplest of structures. Any non-trivial piece of work is far from being self-documenting. Code is self-documenting for the guy who wrote it. I guarantee you that anyone else reading it has to reconstruct a stack in their head to understand what the hell is going on. That's not self-documentation. I shouldn't have to think to understand what a chunk-o-code is doing. The same for functions and/or methods.
The myth of self-documenting code is easy to demonstrate if I show you a piece of code in a language you don't know. I am going to assume that most programmers these days don't know assembler, Forth or Lisp.
I wrote this twenty years ago. Even if you understand Lisp you'd have to think it through. However, this is not how I wrote it. This is what I actually wrote: Even if you don't know Lisp you now have an idea of what this code is doing. My style has changed over the years. This isn't my best example, but it is here to drive a point home.The use of an unfamiliar language serves to illustrate the point that the idea of self-documenting code is, again, a myth. I wrote that code myself and without the comments I'd have to mentally reconstruct every step to even begin to understand what's going on and what the intent was. I haven't touched Lisp in quite some time.
I've looked through so much code in Github without a single comment that it makes me wonder if this is what is being taught in schools these days. Accurate in-code documentation is, as far as I am concerned, part and parcel of becoming a professional programmer. It recognizes that the work represents a huge investment in time, money and intellectual effort and it ensures that this effort and expense doesn't have to be duplicated in order to maintain, evolve or migrate the product as the Microsoft example clearly demonstrates.
[+] [-] yxhuvud|13 years ago|reply
> // Bounds-check velocities
No, that is an example of a comment of a region that should have been a separate method/function.
[+] [-] robomartin|13 years ago|reply
[+] [-] shmerl|13 years ago|reply
http://xbill.org
[+] [-] zwieback|13 years ago|reply
Just played a round of pinball. Despite having used Windows since 2.0, I didn't even know Pinball existed before.
[+] [-] Wingman4l7|13 years ago|reply
[+] [-] jfreak53|13 years ago|reply
What ever you do DO NOT press F4 for fullscreen, it botches dual-monitors on Gnome. Took me 20 min. to get back up and running again.
BUT, Pinball works :)
[+] [-] isabre|13 years ago|reply
[+] [-] AndreyKarpov|13 years ago|reply
[+] [-] forgotAgain|13 years ago|reply