Live data from Hacker News

Why was Pinball removed from Windows Vista?

blogs.msdn.com

101–110 of 149 posts

Re: Why was Pinball removed from Windows Vista?

#102
> 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  THRESHOLD)
        {
         // Limit velocity
         velocity[i] = THRESHOLD;
         ...
That's ridiculous. However, something like this is useful:

    // Bounds-check velocities
    for(int i=0; i  THRESHOLD)
        {
         velocity[i] = THRESHOLD;
         ...
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.

    (defun GetPolylineEndEntities ( plename / plends cvcenter cvsize oldcmdecho pt1 pt2 endss outlist)
        (setq plends (GetPolylineEnds plename))
        (setq cvcenter (getvar "viewctr")
            cvsize   (getvar "viewsize")
            oldcmdecho (getvar "CMDECHO")
        )    
        (setvar "CMDECHO" 0)

        (foreach point plends
         (progn
            (setq pt1 (add2d point '(-0.0125 -0.0125)))
            (setq pt2 (add2d pt1 '(0.025 0.025)))
            (command "zoom" "c" point 2)
            (setq endss (ssdel plename (ssget "C" pt2 pt1)))
            (setq outlist (append outlist (list (ssname endss 0))))
         )
        )
        (command "zoom" "c" cvcenter cvsize)
        (setvar "CMDECHO" oldcmdecho)
        outlist
    )
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.

Re: Why was Pinball removed from Windows Vista?

#103
post #38

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 charact…

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.

Re: Why was Pinball removed from Windows Vista?

#104

> 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 comm…

Assembler makes the point even more apparent:

    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);
        }
    }

Re: Why was Pinball removed from Windows Vista?

#105
post #72

Earlier quoted context omitted.

The point is both Unix and Windows faced the exact same problem: needing to support larger characters. MS thought little and unleashed their army of coders to do something foolish. The Unix guys thought hard and did something elegant and sensible.

A huge amount of Unix software uses UTF-16 just like Windows (including Java). You're just being deliberately ignorant of the history. UTF-8 didn't exist and UCS-2/UTF-16 was the standard . One could argue that the Unicode Consortium screwed up assuming that Basic Multilingual Plane would be enough characters for everyone.

I am not ignorant of the history. Please try to follow this reasoning:

1. Faced with a new character set which was larger than eight-bits (Unicode 16-bit) Microsoft said "hey let's make an all-new API" and set to work rewriting everything

2. Faced with a new character set which was larger than eight-bits (Unicode 32-bit), the Unix guys said "hey let's create a standard way to encode these characters and rewrite nothing.

You seem to be fixated on the difference between the new character sizes. Ignore the precise number of bits! The point is when making a change to adapt to a new system, do you rewrite everything and risk causing bugs everywhere, or do you do something clever which has far less risk and uses the same API?

Re: Why was Pinball removed from Windows Vista?

#106
post #82
post #38

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 charact…

Confusing the Linux kernel service API and the OS API is pretty misleading.

Nope. Windows does not distinguish between a kernel APIs versus other APIs. They are all "The Win{32,64} API"

Re: Why was Pinball removed from Windows Vista?

#107

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…

I was surprised to see that microsoft paid very little to license the game, and even originally tried charging companies to include games.

Re: Why was Pinball removed from Windows Vista?

#108
post #82

Earlier quoted context omitted.

Confusing the Linux kernel service API and the OS API is pretty misleading.

Nope. Windows does not distinguish between a kernel APIs versus other APIs. They are all "The Win{32,64} API"

Again, you are confused with user mode API (Win32/64) and kernel mode service API. Windows does have kernel mode service API. Just it's not well known since most people don't need to deal with them.

Re: Why was Pinball removed from Windows Vista?

#110

Earlier quoted context omitted.

A huge amount of Unix software uses UTF-16 just like Windows (including Java). You're just being deliberately ignorant of the history. UTF-8 didn't exist and UCS-2/UTF-16 was the standard . One could argue that the Unicode Consortium screwed up assuming that Basic Multilingual Plane would be enough characters for everyone.

I am not ignorant of the history. Please try to follow this reasoning: 1. Faced with a new character set which was larger than eight-bits (Unicode 16-bit) Microsoft said "hey let's make an all-new API" and set to work rewriting everything 2. Faced with a new character set which was larger than eight-bits (Unicode 32-bit), the Unix guys said "hey let's create a standard way to encode these characters and rewrite nothi…

Well, remember that they already had to rewrite a lot for Win32 anyway.
Post reply on HN