[stella] How Not To Write A Programming Book

Subject: [stella] How Not To Write A Programming Book
From: Christopher Tumber <christophertumber@xxxxxxxxxx>
Date: Fri, 02 May 2003 00:51:01 -0400
I just finished listing a bunch of books on eBay (spring cleaning and all that..). While doing so, I happened to notice the following. I include more than I really need to because I found it so disturbing...

This is from Apple Machine Language for beginners (Compute!) page 113.

A 6502 Chip Bug

On the other hand, there is another, rather peculiar JMP addressing mode which is hardly, if ever, used in ML: JMP ($5000). This is an _indirect_ jump which works like the indirect addressing we've seen before. Remember that with the indirect,Y addressing mode,  LDA ($81),Y the number in Y is added to the _address_ found in $81 and $82. This address is the _real_ place we are LDAing from, sometimes called the _effective address_. If $81 holds a 00, and $82 holds a $40, and Y holds a 2, the address we LDA from is going to be $4002. Similarily (but without adding Y), the effective address found at the two bytes within the parentheses becomes the place we JMP to in JMP ($5000).

There are no necessary uses for this instruction. Best avoid it in the same way you avoid playing around with the stack until you're an ML expert. If you find it in your computer's BASIC code, it will probably be involved in an "indirect jump table," a series of registers which are dynamic. That is, they can be changed as the program progresses. Such a technique is very close to a self-altering program and would have few applications in ML. But worse than than [sp], there is a bug in the 6502 chip itself which causes the indirect JMP instruction to malfunction under certain circumstances. Just put JMP ($NNNN) into the same category as BPL and BMI. Avoid them.

If you decide that for some reason you must use indirect JMP, be sure to avoid the edge of a page ($ff is on the edge, it's ready to reset to $00), an indirect JMP will correctly use the low byte (LSB) from the pointer at $NNFF, but it will not pick up the high byte (MSB) from $NNFF+1 as it should. Instead it gets the high byte from $NN00.

- First of all, THIS IS NOT A BUG! It's a limitation. Are conditional branch instructions bugged because they can only branch foreward 127 bytes and backwards 128 bytes? Is zero page addressing bugged because it can only address up to $FF? No, indirect addressing is LIMITED to a look-up within a page. That's no big mystery and it's not the only addressing mode to change behaviour accross a page boundary (ie: relative and indexed modes which take longer accross page boundaries). Does he pitch a fit because LDA zeropage,x will wrap around if the effective address goes past $FF? Don't think so...

- "Into the same category as BPL and BMI. Avoid them." WTF? I'm not sure what they have against BPL and BMI, the only thing I can find is, "The reasons for this are exotic. We don't need to go into them. Just be warned that BPL and BMI which sound so logical and useful are not. They can fail you and neither ones lives up to its name. Stick with the always trustworthy BCC, BCS." (Page 99) I *think* it's because they don't want to explain how negative numbers are represented by #$81-#$FF but c'mon - Good thing it's not a 6809 book, imagine having to deal with two's compliments! Scary! Hey, I wonder if there's anything on BCD, bet THAT'S pretty scary too. (Nope, nothing in the index on BCD).

- "avoid playing around with the stack until you're an ML expert". You need to be an expert to use the stack? Huh?

- "There are no necessary uses for this instruction" You can say that for a lot of instructions!! So what? necessary != usefull

- "Such a technique is very close to a self-altering program" No it's not! Trust me, I *love* self-modifying (self-altering bleh!) code and indirect addressing is not 
anywhere close to self-modifying unless VARIABLES and CONDITIONAL BRANCHES are close to self-modifying too! All JMP ($NNNN) really is, is ON X GOTO (in BASIC) or CASE/SWITCH (in C). And the jump vector is almost always in RAM, just like any other variable.

lda LSBtable,x
sta $80
lda MSBtable,x
sta $81
jmp ($0080)

Is the same as:

On X goto 100,200,300,400,500,600,700,800


Switch (X) {
 case 1: <some code>;break;
 case 2: <some code>;break;
 case 3: <some code>;break;

Yeh, that's a pretty obscure concept, that!

- "would have few applications in ML". No!! It's an extremely powerful "super conditional branch", used like ON GOTO/SWITCH-CASE. It's particularly usefull for sub-routine 
scheduling when you have more routines than you have time for during one frame or interrupt (or whatever period of time) - ie: Check if there's enough time left during the hardware/timer interupt or during Vertical Blank/Overscan, if there is call the next routine from a list (otherwise return from the interupt or end the Vertical blank/Overscan). Next interupt/Vertical Blank/Overscan proceed on down the list. Or, you can use it for code which may or may not need to be called. For example, let's say a game has 4 players. Each player can be played by the computer or by a person. At the begining of a game, computer/person players are selected. This becomes really easy to handle if you use an indirect JMP. For each player, you setup an indirect JMP vector. If it's a computer player, the JMP vector points to the subroutine to make a computer move. If it's a person, you set the JMP vector to point to the routine to input a move from the player. For each player's "turn" you just have to JMP (Player1TypeVector) JMP (Player2TypeVector) etc and there's not decision making logic involved. Or, another use is if you have a random AI (or a variety of random even generators like a random level design or random level sequence). Generate a random number and which routine is called is determined by the random number through an ON X GOTO type branching as above:


JSR RandomNumber
lda LSBEnemyAItable,x
sta $80
lda MSBEnemyAItable,x
sta $81
jmp ($0080)

which would be the same as:

Switch (X) {
 case 1: MoveUp(EnemyStruct);break;
 case 2: MoveDown(EnemyStruct);break;
 case 3: MoveRight(EnemyStruct);break;
 case 4: MoveLeft(EnemyStruct);break;


GOSUB RandomNumber
ON X GOTO 100,200,300,400

And I'm sure there's all kinds of other uses you could come up with. Like, you have several different routines for drawing each level. Instead of doing a bunch of CMPs to determine which level drawing routine to call, you setup a JMP vector and when it's time to draw the level, you just JMP ($LevelDrawRoutineVector). Generally, if you're not hurting for RAM many sequences of related CMP branches can be replaced with this kind of branching, particularly if the value being tested does not change often so you can setup the JMP vector once and leave it be...

There's a reason later CPU's like the 6809 added an indirect JSR too! Because it's such a usefull addressing mode!!

- But my main beef - I understand this is an introductory text, but why, why, why would you try to scare someone off like that? If you don't want to tackle it, fine, skip it. But why put it in someone's head that there's this big scary addressing mode that's going to bite them? What's the good in that? (Even worse with that snippet about BPL and BMI - Don't use it, scary! But nevermind why...) Un-freaking believable!

Sorry, just had to vent a little bit there...


- Otherwise, I'm sure it's a very fine book! Go bid on my auction (smile).

Archives (includes files) at http://www.biglist.com/lists/stella/archives/
Unsub & more at http://www.biglist.com/lists/stella/

Current Thread