Saturday, March 7, 2015

Tiny 6502 - a Javascript 6502 Emulator for the NES

6502 Processor Pinouts
It's been some time since I updated this blog. Work has been keeping me extremely busy. I just got off a very long term project and my brain is fried. So what does a computer nerd do for rest and relaxation? Why think about console game coding of course. For some time I have considered what it would take to code either a 6502, pronounced sixty-five-oh-two, emulator for the NES or a RISC emulator for the N64. During this time obviously, many platform emulators in javascript have hit the web. However, not a lot of tutorials on the web on how to write an emulator. I am going to write a series of blogs on writing a 6502 processor emulator, called Tiny6502, for edification and learning. I call it Tiny 6502 because we're going to build it in stages and in software modules.

Phase I we're going to build the Tiny 6502 emulator libraries, Phase II, we're going to build a virtual hobby programmer board, like the KIM-1, to program the virtual processor, and Phase III we're going to implement a ROM like library to allow the Tiny6502 emulator to run NES code. So why write a virtual emulator?

Because it beats the hell out of wire wrapping circuits any day!



Wire Wrapped NES Development Board
Besides, we have so much computing power in the palm of our hands everyday, it is mind blowing that I can code my cell phone in javascript and HTML 5. -And then run the same code on my monstrous PowerMac desktop, which has more processing power than the CRAY computer I was trained on in my college days! Ah, the brave new world we live in. This endeavor started with some great articles I found on the web for designing my virtual processor architecture. The NES and Atari dev forums and blogs are rich with information for back engineering. Some of these articles we're going to re-visit to implement some of their coding tutorials into the Tiny 6502. There are a lot of libraries we'er going to have to write along the way to get to the NES emulator up and running. The first order of business is where to begin.

The 6502 is a simple processor, it made Jobs and Wozniak very wealthy nerds in the 70's and 80's. Steve Jobs went on to become the icon of the computer nerd and he had countless millions. I don't think I 'll make millions of my Tiny 6502 virtual processor, that ship has sailed. However, in returning to my design mindset, I was going to first write code to emulate the accumulator and general purpose registers. Then it donned on my I would need memory to fetch and write to. So I next anticipated writing a memory library for the Tiny 6502. As I did more research on batting back and forth between the accumulator and general purpose registers, or writing the memory lib, it suddenly made more sense, after some research, to write the code for the 6502's status register first. Why? Well, because operations of the 6502 processor depended on states in the status bit register, which affected the operations in the accumulator and the X and Y general purpose registers.

6502 Microprocessor Architecture


The NES Dev Wiki has a fairly good explanation of the status register, sometimes called the P, for the Processor Status register. The task at hand, and our goal for coding, is outlined very clearly in this online article I found over at the Atari Archives.

Processor Status Register Bits
I am going to use the article at the Atari Archives site as my model for coding the Processor Status register. Obviously, our code is going to have to do some low level binary operations to toggle bits in our software emulated P register.

Toggling Bits in Javascript

I had to brush off my TTL logic skillz from days of old. But I managed to create a simple library for this. I needed a library to set an individual bit and at location n. I also needed to clear a a bit at location n. Finally I needed to test a bit a position n to determine if the bit was set. For a good refresher on simple bit toggling, read low level bit hacks you absolutely must know. I used this article to write my bit_lib.js library.

Setting Bits in Javascript

Here is some basic TTL logic, or to a programmer, bitwise logical operations, we will need to understand in order to set bits. From the low level bit hacking article, in order to set a bit we must perform the following procedure in our software.

In order to set bit at position n in a binary number we must create a binary mask. The binary mask is created by shifting bits n times to the left to reached the desired bit.

1<<0      00000001  
1<<1      00000010
1<<2      00000100
1<<3      00001000
1<<4      00010000
1<<5      00100000
1<<6      01000000
1<<7      10000000

If I want to set bit 5 in binary number, I must create a mask by rotating bits left 5 times.

bit mask for bit 5:   mask = 1<<5  = 00100000

As an example, let's set bit 5 in the binary number 1000 0000 (128 base 10 - decimal).

        1000 0000               128 decimal

We will need our bit mask for bit position 5, from the table above.

        0010 0000               1<<5

With these two values we will now set bit 5 in the binary number 128, using a logical OR operator.

TTL Logical OR

Let's apply the logical OR to our example of setting bit 5 in the binary number (128 deicmal).
The logical OR is represented in javascript with the "|" operator. I am using the TTL logical OR table above to determine the output of the operation.

         1000 0000               128 decimal
|        0010 0000               1<<5 (bit position 5 mask)
------------------------------------------------
        1010  0000               -this is the resulting binary number (160 decimal)

Our coding task for this is the following:

1. Create a mask for bit position n.

  mask = (1 << bit position)

2. Carry out a logical OR on the mask and the bits.

  binary number | mask;

3. Simplify the operation,

   binary number | (1 << bit position)

The javascript code for setbit(), from the bit_lib.js, library is shown in Listing 1.


Listing 1. Setting a bit in javascript
1:  /*------------------------------------------------------------------------  
2:  * function: setbit (bitstring, bitposition)  
3:  *  
4:  * description:  
5:  *  This javascript function sets the individual bit in a bitstring at  
6:  *  at bitposition in the bitstring.  
7:  *  
8:  * Example: Set bit position 2 in bit string 01111000  
9:  *  
10:  *   01111000  (120 in binary)   argument: bitstring = 01111000  
11:  *  | 00000100  (1<<2)       argument: bitposition = 2  
12:  *   --------  
13:  *   01111100            returns: this value  
14:  *  
15:  * Arguments:  
16:  *  bitstring: the binary representation of the bits to be modified   
17:  *  bitposition: the n-th bit to set  
18:  *------------------------------------------------------------------------*/  
19:  function setbit(bitstring, bitposition) {  
20:   // set bit  
21:   // num | (1<<bit);  
22:   return bitstring | (1<<bitposition);  
23:  }   

Clearing Bits in Javascript

The trick in clearing a bit at position n in a binary number requires creating a 1's complement mask.
From our previous example in setting bits. We only needed a basic mask on the bit position to set.
Remember, setting bit 5 in the decimal number 128 we created a bit mask.

mask = 1<<5  = 00100000

Now we our desire is to clear the bit. To do so we need an binary inverted mask, called a one's complement mask. The following are one's complement masks for bits 0-7.

~1            11111110  (same as ~(1<<0))
~(1<<1)   11111101
~(1<<2)   11111011
~(1<<3)   11110111
~(1<<4)   11101111
~(1<<5)   11011111
~(1<<6)   10111111
~(1<<7)   01111111


mask = one's complement of (1<<5) 
         = one's complement of 00100000
         =                                   11011111 

We flipped the bits in the binary number.

TTL Logical AND
Let's apply the logical AND to our example of clearing bit 5 in the binary number (128 deicmal).
The logical AND is represented in javascript with the "&" operator. I am using the TTL logical AND table above to determine the output of the operation.

         1111 0000               240 decimal
&      1101 1111               ~(1<<5 )  one's complement for bit position 5 mask
------------------------------------------------
         1101  0000               -this is the resulting binary number (208 decimal)


How do we accomplish this is javascript? We use the tilde ~ operator.

Our coding task for this is the following:

1. Create a one's complement mask for bit position n using the tilde ~ operator.

  mask = ~ (1 << bit position)

2. Carry out a logical AND on the one's complement mask and the bits.

  binary number & mask;

3. Simplify the operation,

   binary number & ~(1 << bit position)

The javascript code for clearbit(), from the bit_lib.js, library is shown in Listing 2.

Listing 2. Clearing a bit in javascript
1:  /*------------------------------------------------------------------------  
2:  * function: clearbit (bitstring, bitposition)  
3:  *  
4:  * description:  
5:  *  This javascript function clears the individual bit in a bitstring at  
6:  *  at bitposition in the bitstring.  
7:  *  
8:  * Example: Clear bit position 4 in bit string 01111111  
9:  *  
10:  *   01111111  (127 in binary)   argument: bitstring = 01111111  
11:  *  & 11101111  (~(1<<4))      argument: bitposition = 4  
12:  *   --------  
13:  *   01101111            returns: this value  
14:  *  
15:  * Arguments:  
16:  *  bitstring: the bit string to be modified   
17:  *  bitposition: the n-th bit to set  
18:  *------------------------------------------------------------------------*/  
19:  function clearbit(bitstring, bitposition) {  
20:   // unset bits  
21:   // num & ~(1<<bit);  
22:   return bitstring & ~(1<<bitposition);  
23:  }   

Listing 3. Checking to see if a bit is set in javascript
1:  /*------------------------------------------------------------------------  
2:  * function: isBitNSet (bitstring, bitposition)  
3:  *  
4:  * description:  
5:  *  This javascript function clears the individual bit in a bitstring at  
6:  *  at bitposition in the bitstring.  
7:  *  
8:  * Example: Test to see if bit position 0 in bit string 01100010 is set  
9:  *  
10:  *   01100010  (98 in binary) argument: bitstring = 01100010  
11:  * &  00000001          argument: bitposition = 0  
12:  *   --------  
13:  *   00000000          
14:  *  
15:  *  if (bitstring & (1<<bitposition)) {  
16:  *  n-th bit position is set  
17:  *  }  
18:  *  else {  
19:  *   n-th bit position is not set  
20:  *  }  
21:  *  
22:  * Arguments:  
23:  *  bitstring: the bit string to be modified   
24:  *  bitposition: the n-th bit to set  
25:  *  
26:  * Returns:  
27:  *  1 - if bit at bitposition is set. ZERO otherwise  
28:  *------------------------------------------------------------------------*/  
29:  function isBitNSet(bitstring, bitposition) {  
30:   if ( bitstring & (1<<bitposition) ) {  
31:        return 1;  
32:   }   
33:   // bit not set return FALSE!  
34:   return 0;  
35:  }   

Coding the P Register in Javascript

With some simple bit toggling tools we're going to start writing some code for our processor status register emulation. Our criteria is to set the following bits in the P register.

   Processor Status
   ----------------
   
   The processor status register is not directly accessible by any 6502 
   instruction.  Instead, there exist numerous instructions that test the 
   bits of the processor status register.  The flags within the register 
   are:
   
   
       bit ->   7                           0
              +---+---+---+---+---+---+---+---+
              | N | V |   | B | D | I | Z | C |  <-- flag, 0/1 = reset/set
              +---+---+---+---+---+---+---+---+
              
              
       N  =  NEGATIVE. Set if bit 7 of the accumulator is set.
       
       V  =  OVERFLOW. Set if the addition of two like-signed numbers 
             or the subtraction of two unlike-signed numbers produces 
             a result greater than +127 or less than -128.
             
       B  =  BRK COMMAND. Set if an interrupt caused by a BRK, reset if
             caused by an external interrupt.
             
       D  =  DECIMAL MODE. Set if decimal mode active.
       
       I  =  IRQ DISABLE.  Set if maskable interrupts are disabled.
             
       Z  =  ZERO.  Set if the result of the last operation (load/inc/dec/
             add/sub) was zero.
             
       C  =  CARRY. Set if the add produced a carry, or if the subtraction
             produced a borrow.  Also holds bits after a logical shift.
             

With this information we can start looking at coding the P register emulator.

Listing 4. The status register bit fields in javascript
1:  /*-------------------------------------------------------------------------------  
2:  *  
3:  *  
4:  *  6502 CPU Status Bit Register library  
5:  *  
6:  * notes: http://nesdev.com/6502.txt  
7:  * THE STATUS REGISTER  
8:  *  
9:  *  This register consists of eight "flags" (a flag = something that indi-  
10:  * cates whether something has, or has not occurred). Bits of this register  
11:  * are altered depending on the result of arithmetic and logical operations.  
12:  * These bits are described below:  
13:  *  
14:  *   Bit No.    7  6  5  4  3  2  1  0  
15:  *              S  V     B  D  I  Z  C  
16:  *  
17:  *  Bit0 - C - Carry flag: this holds the carry out of the most significant  
18:  *  bit in any arithmetic operation. In subtraction operations however, this  
19:  *  flag is cleared - set to 0 - if a borrow is required, set to 1 - if no  
20:  *  borrow is required. The carry flag is also used in shift and rotate  
21:  *  logical operations.  
22:  *  
23:  *  Bit1 - Z - Zero flag: this is set to 1 when any arithmetic or logical  
24:  *  operation produces a zero result, and is set to 0 if the result is  
25:  *  non-zero.  
26:  *  
27:  *  Bit 2 - I: this is an interrupt enable/disable flag. If it is set,  
28:  *  interrupts are disabled. If it is cleared, interrupts are enabled.  
29:  *  
30:  *  Bit 3 - D: this is the decimal mode status flag. When set, and an Add with  
31:  *  Carry or Subtract with Carry instruction is executed, the source values are  
32:  *  treated as valid BCD (Binary Coded Decimal, eg. 0x00-0x99 = 0-99) numbers.  
33:  *  The result generated is also a BCD number.  
34:  *  
35:  *  Bit 4 - B: this is set when a software interrupt (BRK instruction) is  
36:  *  executed.  
37:  *  
38:  *  Bit 5: not used. Supposed to be logical 1 at all times.  
39:  *  
40:  *  Bit 6 - V - Overflow flag: when an arithmetic operation produces a result  
41:  *  too large to be represented in a byte, V is set.  
42:  *  
43:  *  Bit 7 - S - Sign flag: this is set if the result of an operation is  
44:  *  negative, cleared if positive.  
45:  *  
46:  *  The most commonly used flags are C, Z, V, S.  
47:  *  
48:  *-------------------------------------------------------------------------------*/  
49:  var StatusRegister = 0;  
50:  var CBit = 0;  // Carry Flag   Bit 0  
51:  var ZBit = 1;  // Zero Flag   Bit 1  
52:  var IBit = 2;  // Interrupt Flag Bit 2  
53:  var DBit = 3;  // BCD Flag    Bit 3  - binary coded decimal  
54:  var BBit = 4;  // BRK Int Flag  Bit 4 - BRK instruction was executed  
55:  var NBit = 5;  // Not used    Bit 5 - not used, always set to 1  
56:  var VBit = 6;  // OVRFL Flag   Bit 6 - overflow flag  
57:  var SBit = 7;  // Sign flag   Bit 7 - set if result of operation is negative  

Now we can look at some code on how to set a bit in our status register emulator. You can see how our code for the status register is actually a help function using the setbit() function from the library, bitlib.js.

Listing 5. Setting bits in our P reg using javascript.

1:  /*-------------------------------------------------------------------------------  
2:  * function: SetStatusRegisterBit (bit_position)  
3:  *  
4:  * description:  
5:  *  This javascript function sets the individual bit flags   
6:  *  in the 6502 status register emulator.  
7:  *  
8:  *  CBit = 0;  // Carry Flag   Bit 0  
9:  *  ZBit = 1;  // Zero Flag   Bit 1  
10:  *  IBit = 2;  // Interrupt Flag Bit 2  
11:  *  DBit = 3;  // BCD Flag    Bit 3  - binary coded decimal  
12:  *  BBit = 4;  // BRK Int Flag  Bit 4 - BRK instruction was executed  
13:  *  NBit = 5;  // Not used    Bit 5 - not used, always set to 1  
14:  *  VBit = 6;  // OVRFL Flag   Bit 6 - overflow flag  
15:  *  SBit = 7;  // Sign flag   Bit 7 - set if result of operation is negative  
16:  *   
17:  * Example: Set ZBit in Status Register  
18:  *  
19:  *  SetStatusRegisterBit (Zbit);  
20:  *  ! Z-Bit SET DEBUG: 1111111100000010  
21:  *                  ^ Zbit set  
22:  *  
23:  * Arguments:  
24:  *  bit_position: the n-th bit to set (Processor flag bit position)  
25:  * Returns:  
26:  *  None - sets member variable StatusRegister  
27:  *-------------------------------------------------------------------------------*/  
28:  function SetStatusRegisterBit(bit_position){  
29:   StatusRegister = setbit(StatusRegister, bit_position);  
30:  }  


Using a helper function with clearbit() we can also clear bits in the P register.


Listing 6. Clearing bits in our P reg using javascript.

1:  /*-------------------------------------------------------------------------------  
2:  * function: ClearStatusRegisterBit (bit_position)  
3:  *  
4:  * description:  
5:  *  This javascript function clears the individual bit flags   
6:  *  in the 6502 status register emulator.  
7:  *  
8:  * Example: Clear ZBit in Status Register  
9:  *  
10:  *  ClearStatusRegisterBit (Zbit);  
11:  *  ! Z-Bit CLEAR DEBUG: 1111111100000000  
12:  *                   ^ Zbit clear  
13:  *  
14:  * Arguments:  
15:  *  bit_position: the n-th bit to set (Processor flag bit position)  
16:  * Returns:  
17:  *  None - sets member variable StatusRegister  
18:  *-------------------------------------------------------------------------------*/  
19:  function ClearStatusRegisterBit(bit_position) {  
20:       StatusRegister = clearbit(StatusRegister, bit_position);  
21:  }  
Listing 7. Checking to see if bits are set in our P reg using javascript.

1:  /*-------------------------------------------------------------------------------  
2:  * function: IsStatusRegisterBitSet (bit_position)  
3:  *  
4:  * description:  
5:  *  This javascript function tests to see if the individual bit flags   
6:  *  in the 6502 status register emulator are set.  
7:  *  
8:  * Example: Test to see if ZBit in Status Register is set.  
9:  *  
10:  *  SetStatusRegisterBit (Zbit);  
11:  *  ! Z-Bit SET DEBUG: 1111111100000010  
12:  *                  ^ Zbit set  
13:  *  bZbitSet = IsStatusRegisterBitSet(Zbit);   
14:  *  ! Z-Bit SET? DEBUG: 1  
15:  *  
16:  *  ClearStatusRegisterBit (Zbit);  
17:  *  ! Z-Bit CLEAR DEBUG: 1111111100000000  
18:  *                   ^ Zbit clear  
19:  *  bZbitSet = IsStatusRegisterBitSet(Zbit);   
20:  *  ! Z-Bit SET? DEBUG: 0  
21:  *  
22:  * Arguments:  
23:  *  bit_position: the n-th bit to set (Processor flag bit position)  
24:  * Returns:  
25:  *  1 - if bit is set, ZERO otherwise  
26:  *-------------------------------------------------------------------------------*/  
27:  function IsStatusRegisterBitSet(bit_position) {  
28:       return isBitNSet(StatusRegister, bit_position);  
29:  }  

That's it. Now we need some driver code to call from HTML. The HTML isn't pretty and polished. We're just using it so we can write to the browser and see values in the Chrome debugger.
Included in Source 1 is the sample HTML file I am using for testing.

This HTML file will generate the following output to your browser.

! C-Bit Set DEBUG: 1111111100000001
! Z-Bit Set DEBUG: 1111111100000011
! I-Bit Set DEBUG: 1111111100000111
! D-Bit Set DEBUG: 1111111100001111
! B-Bit Set DEBUG: 1111111100011111
! B-Bit Set DEBUG: 1111111100111111
! B-Bit Set DEBUG: 1111111101111111
! B-Bit Set DEBUG: 1111111111111111
! C-Bit CLR DEBUG: 1111111111111110
! C-Bit CLR DEBUG: 1111111111111110
! C-Bit2 SET DEBUG: 1111111111111111
! Z-Bit SET DEBUG: 1111111100000010
! Z-Bit SET? DEBUG: 1
! Z-Bit CLEAR DEBUG: 1111111100000000
! Z-Bit SET? DEBUG: 0
0000: a9 00 20 10 00 4c 2 00 00 00 00 00 00 00 00 40 e8 00 20 10 00 4c 2 00 00 00 00 00 00 00 00 40

Again this is code straight off the workbench. You will see the start of my design for the 8-bit memory. The bottom line of the output is my initial implementation of memory in the Tiny 6502.

Here is the complete source listing for the two javascript libraries and HTML driver file you will need to add to your sandbox to play along.

In the next installment we will start playing with memory and the accumulator.

Until then....   happy console hacking!

Mike

Source Listing 1. HTML driver code for testing Tiny6502.

1:  <html>  
2:  <head>  
3:  <!-- import javascript libraries -->  
4:  <script type="text/javascript"src="bit_lib.js"></script>  
5:  <script type="text/javascript"src="cpu6502.js"></script>  
6:  <script type="text/javascript">  
7:   function initStatusRegister() {  
8:   var radix = 2;  
9:   var mask = parseInt("0",radix);  
10:   var flag = 0xFF00;  
11:   StatusRegister = flag | mask;  
12:   }  
13:  initStatusRegister();  
14:  //SetCBit();  
15:  //SetStatusRegister(CBitMask);  
16:  StatusRegister = setbit(StatusRegister, 0);  
17:  document.write("!  C-Bit Set DEBUG: " + StatusRegister.toString(2) + "<br>");  
18:  /*  
19:  ClearCBit();  
20:  document.write("!  C-Bit Clr DEBUG: " + StatusRegister.toString(2) + "<br>");  
21:  */  
22:  //SetZBit();  
23:  SetStatusRegister(ZBitMask);  
24:  document.write("!  Z-Bit Set DEBUG: " + StatusRegister.toString(2) + "<br>");  
25:  //SetIBit();  
26:  SetStatusRegister(IBitMask);  
27:  document.write("!  I-Bit Set DEBUG: " + StatusRegister.toString(2) + "<br>");  
28:  //SetDBit();  
29:  SetStatusRegister(DBitMask);  
30:  document.write("!  D-Bit Set DEBUG: " + StatusRegister.toString(2) + "<br>");  
31:  //SetBBit();  
32:  SetStatusRegister(BBitMask);  
33:  document.write("!  B-Bit Set DEBUG: " + StatusRegister.toString(2) + "<br>");  
34:  //Set-Bit();  
35:  SetStatusRegister(mBitMask);  
36:  document.write("!  B-Bit Set DEBUG: " + StatusRegister.toString(2) + "<br>");  
37:  //SetBBit();  
38:  SetStatusRegister(VBitMask);  
39:  document.write("!  B-Bit Set DEBUG: " + StatusRegister.toString(2) + "<br>");  
40:  //SetBBit();  
41:  SetStatusRegister(NBitMask);  
42:  document.write("!  B-Bit Set DEBUG: " + StatusRegister.toString(2) + "<br>");  
43:  StatusRegister = clearbit(StatusRegister, 0);  
44:  document.write("!  C-Bit CLR DEBUG: " + StatusRegister.toString(2) + "<br>");  
45:  StatusRegister = clearbit(StatusRegister, 0);  
46:  document.write("!  C-Bit CLR DEBUG: " + StatusRegister.toString(2) + "<br>");  
47:  SetStatusRegisterBit(CBit);  
48:  document.write("!  C-Bit2 SET DEBUG: " + StatusRegister.toString(2) + "<br>");  
49:  initStatusRegister();  
50:  SetStatusRegisterBit(ZBit);  
51:  document.write("!  Z-Bit SET DEBUG: " + StatusRegister.toString(2) + "<br>");  
52:  document.write("!  Z-Bit SET? DEBUG: " + isBitNSet(StatusRegister, ZBit) + "<br>");  
53:  ClearStatusRegisterBit(ZBit);  
54:  document.write("!  Z-Bit CLEAR DEBUG: " + StatusRegister.toString(2) + "<br>");  
55:  document.write("!  Z-Bit SET? DEBUG: " + isBitNSet(StatusRegister, ZBit) + "<br>");  
56:  InitMemory ();  
57:  document.write(" " + memdump() + "<br>");  
58:  /*  
59:  toggle_bit(5);  
60:  document.write("!  DEBUG: " + get_bit_mask(5) + "<br>");  
61:  var mask = FLAG_A | FLAG_B | FLAG_D; // 0001 | 0010 | 1000 => 1011  
62:  document.write("!  DEBUG: (mask)" + mask + " " + mask.toString(2) + "<br>");  
63:  */  
64:  </script>  
65:  </head>  
66:  <body>  
67:  </body>  
68:  </html>  


Source 2. bit_lib.js
1:  /*------------------------------------------------------------------------  
2:  * Name:    bit_lib.js  
3:  * Purpose:       javascript bit operator tools  
4:  *  
5:  * Author:   Michael Norton  
6:  *  
7:  * Created:   05/03/2015  
8:  * Copyright:  (c) Michael Norton 2015  
9:  * Licence:   <your licence>  
10:  *  
11:  * Notes:  
12:  *       Code based on bit hacks discussion at:  
13:  *  http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/  
14:  *  
15:  *------------------------------------------------------------------------*/  
16:  /*------------------------------------------------------------------------  
17:  * function: setbit (bitstring, bitposition)  
18:  *  
19:  * description:  
20:  *  This javascript function sets the individual bit in a bitstring at  
21:  *  at bitposition in the bitstring.  
22:  *  
23:  * Example: Set bit position 2 in bit string 01111000  
24:  *  
25:  *   01111000  (120 in binary)   argument: bitstring = 01111000  
26:  *  | 00000100  (1<<2)       argument: bitposition = 2  
27:  *   --------  
28:  *   01111100            returns: this value  
29:  *  
30:  * Arguments:  
31:  *  bitstring: the binary representation of the bits to be modified   
32:  *  bitposition: the n-th bit to set  
33:  *------------------------------------------------------------------------*/  
34:  function setbit(bitstring, bitposition) {  
35:   // set bit  
36:   // num | (1<<bit);  
37:   return bitstring | (1<<bitposition);  
38:  }   
39:  /*------------------------------------------------------------------------  
40:  * function: clearbit (bitstring, bitposition)  
41:  *  
42:  * description:  
43:  *  This javascript function clears the individual bit in a bitstring at  
44:  *  at bitposition in the bitstring.  
45:  *  
46:  * Example: Clear bit position 4 in bit string 01111111  
47:  *  
48:  *   01111111  (127 in binary)   argument: bitstring = 01111111  
49:  *  & 11101111  (~(1<<4))      argument: bitposition = 4  
50:  *   --------  
51:  *   01101111            returns: this value  
52:  *  
53:  * Arguments:  
54:  *  bitstring: the bit string to be modified   
55:  *  bitposition: the n-th bit to set  
56:  *------------------------------------------------------------------------*/  
57:  function clearbit(bitstring, bitposition) {  
58:   // unset bits  
59:   // num & ~(1<<bit);  
60:   return bitstring & ~(1<<bitposition);  
61:  }   
62:  /*------------------------------------------------------------------------  
63:  * function: isBitNSet (bitstring, bitposition)  
64:  *  
65:  * description:  
66:  *  This javascript function clears the individual bit in a bitstring at  
67:  *  at bitposition in the bitstring.  
68:  *  
69:  * Example: Test to see if bit position 0 in bit string 01100010 is set  
70:  *  
71:  *   01100010  (98 in binary) argument: bitstring = 01100010  
72:  * &  00000001          argument: bitposition = 0  
73:  *   --------  
74:  *   00000000          
75:  *  
76:  *  if (bitstring & (1<<bitposition)) {  
77:  *  n-th bit position is set  
78:  *  }  
79:  *  else {  
80:  *   n-th bit position is not set  
81:  *  }  
82:  *  
83:  * Arguments:  
84:  *  bitstring: the bit string to be modified   
85:  *  bitposition: the n-th bit to set  
86:  *  
87:  * Returns:  
88:  *  1 - if bit at bitposition is set. ZERO otherwise  
89:  *------------------------------------------------------------------------*/  
90:  function isBitNSet(bitstring, bitposition) {  
91:   if ( bitstring & (1<<bitposition) ) {  
92:        return 1;  
93:   }   
94:   // bit not set return FALSE!  
95:   return 0;  
96:  }   

Source 3. Tiny 6502 library


1:  /*-------------------------------------------------------------------------------  
2:  * Name:    cpu6502.js  
3:  * Purpose:       javascript Tiny 6502 processor emulator  
4:  *  
5:  * Author:   Michael Norton  
6:  *  
7:  * Created:   05/03/2015  
8:  * Copyright:  (c) Michael Norton 2015  
9:  * Licence:   <your licence>  
10:  *  
11:  * Notes:  
12:  * https://github.com/6502/js6502/blob/master/6502.js  
13:  *-------------------------------------------------------------------------------*/  
14:  // set the system memory to 8-bit with memsize of 65536 bytes  
15:  var MaxSystemMemory = 65536;  
16:  var mem8bit = new Uint8Array(MaxSystemMemory);  
17:  /*-------------------------------------------------------------------------------  
18:  *  
19:  *  
20:  *  6502 CPU Status Bit Register library  
21:  *  
22:  * notes: http://nesdev.com/6502.txt  
23:  * THE STATUS REGISTER  
24:  *  
25:  *  This register consists of eight "flags" (a flag = something that indi-  
26:  * cates whether something has, or has not occurred). Bits of this register  
27:  * are altered depending on the result of arithmetic and logical operations.  
28:  * These bits are described below:  
29:  *  
30:  *   Bit No.    7  6  5  4  3  2  1  0  
31:  *          S  V    B  D  I  Z  C  
32:  *  
33:  *  Bit0 - C - Carry flag: this holds the carry out of the most significant  
34:  *  bit in any arithmetic operation. In subtraction operations however, this  
35:  *  flag is cleared - set to 0 - if a borrow is required, set to 1 - if no  
36:  *  borrow is required. The carry flag is also used in shift and rotate  
37:  *  logical operations.  
38:  *  
39:  *  Bit1 - Z - Zero flag: this is set to 1 when any arithmetic or logical  
40:  *  operation produces a zero result, and is set to 0 if the result is  
41:  *  non-zero.  
42:  *  
43:  *  Bit 2 - I: this is an interrupt enable/disable flag. If it is set,  
44:  *  interrupts are disabled. If it is cleared, interrupts are enabled.  
45:  *  
46:  *  Bit 3 - D: this is the decimal mode status flag. When set, and an Add with  
47:  *  Carry or Subtract with Carry instruction is executed, the source values are  
48:  *  treated as valid BCD (Binary Coded Decimal, eg. 0x00-0x99 = 0-99) numbers.  
49:  *  The result generated is also a BCD number.  
50:  *  
51:  *  Bit 4 - B: this is set when a software interrupt (BRK instruction) is  
52:  *  executed.  
53:  *  
54:  *  Bit 5: not used. Supposed to be logical 1 at all times.  
55:  *  
56:  *  Bit 6 - V - Overflow flag: when an arithmetic operation produces a result  
57:  *  too large to be represented in a byte, V is set.  
58:  *  
59:  *  Bit 7 - S - Sign flag: this is set if the result of an operation is  
60:  *  negative, cleared if positive.  
61:  *  
62:  *  The most commonly used flags are C, Z, V, S.  
63:  *  
64:  *-------------------------------------------------------------------------------*/  
65:  var StatusRegister = 0;  
66:  var CBit = 0;  // Carry Flag   Bit 0  
67:  var ZBit = 1;  // Zero Flag   Bit 1  
68:  var IBit = 2;  // Interrupt Flag Bit 2  
69:  var DBit = 3;  // BCD Flag    Bit 3  - binary coded decimal  
70:  var BBit = 4;  // BRK Int Flag  Bit 4 - BRK instruction was executed  
71:  var NBit = 5;  // Not used    Bit 5 - not used, always set to 1  
72:  var VBit = 6;  // OVRFL Flag   Bit 6 - overflow flag  
73:  var SBit = 7;  // Sign flag   Bit 7 - set if result of operation is negative  
74:  /*-------------------------------------------------------------------------------  
75:  * function: SetStatusRegisterBit (bit_position)  
76:  *  
77:  * description:  
78:  *  This javascript function sets the individual bit flags   
79:  *  in the 6502 status register emulator.  
80:  *  
81:  *  CBit = 0;  // Carry Flag   Bit 0  
82:  *  ZBit = 1;  // Zero Flag   Bit 1  
83:  *  IBit = 2;  // Interrupt Flag Bit 2  
84:  *  DBit = 3;  // BCD Flag    Bit 3  - binary coded decimal  
85:  *  BBit = 4;  // BRK Int Flag  Bit 4 - BRK instruction was executed  
86:  *  NBit = 5;  // Not used    Bit 5 - not used, always set to 1  
87:  *  VBit = 6;  // OVRFL Flag   Bit 6 - overflow flag  
88:  *  SBit = 7;  // Sign flag   Bit 7 - set if result of operation is negative  
89:  *   
90:  * Example: Set ZBit in Status Register  
91:  *  
92:  *  SetStatusRegisterBit (Zbit);  
93:  *  ! Z-Bit SET DEBUG: 1111111100000010  
94:  *                  ^ Zbit set  
95:  *  
96:  * Arguments:  
97:  *  bit_position: the n-th bit to set (Processor flag bit position)  
98:  * Returns:  
99:  *  None - sets member variable StatusRegister  
100:  *-------------------------------------------------------------------------------*/  
101:  function SetStatusRegisterBit(bit_position){  
102:   StatusRegister = setbit(StatusRegister, bit_position);  
103:  }  
104:  /*-------------------------------------------------------------------------------  
105:  * function: ClearStatusRegisterBit (bit_position)  
106:  *  
107:  * description:  
108:  *  This javascript function clears the individual bit flags   
109:  *  in the 6502 status register emulator.  
110:  *  
111:  * Example: Clear ZBit in Status Register  
112:  *  
113:  *  ClearStatusRegisterBit (Zbit);  
114:  *  ! Z-Bit CLEAR DEBUG: 1111111100000000  
115:  *                   ^ Zbit clear  
116:  *  
117:  * Arguments:  
118:  *  bit_position: the n-th bit to set (Processor flag bit position)  
119:  * Returns:  
120:  *  None - sets member variable StatusRegister  
121:  *-------------------------------------------------------------------------------*/  
122:  function ClearStatusRegisterBit(bit_position) {  
123:       StatusRegister = clearbit(StatusRegister, bit_position);  
124:  }  
125:  /*-------------------------------------------------------------------------------  
126:  * function: IsStatusRegisterBitSet (bit_position)  
127:  *  
128:  * description:  
129:  *  This javascript function tests to see if the individual bit flags   
130:  *  in the 6502 status register emulator are set.  
131:  *  
132:  * Example: Test to see if ZBit in Status Register is set.  
133:  *  
134:  *  SetStatusRegisterBit (Zbit);  
135:  *  ! Z-Bit SET DEBUG: 1111111100000010  
136:  *                  ^ Zbit set  
137:  *  bZbitSet = IsStatusRegisterBitSet(Zbit);   
138:  *  ! Z-Bit SET? DEBUG: 1  
139:  *  
140:  *  ClearStatusRegisterBit (Zbit);  
141:  *  ! Z-Bit CLEAR DEBUG: 1111111100000000  
142:  *                   ^ Zbit clear  
143:  *  bZbitSet = IsStatusRegisterBitSet(Zbit);   
144:  *  ! Z-Bit SET? DEBUG: 0  
145:  *  
146:  * Arguments:  
147:  *  bit_position: the n-th bit to set (Processor flag bit position)  
148:  * Returns:  
149:  *  1 - if bit is set, ZERO otherwise  
150:  *-------------------------------------------------------------------------------*/  
151:  function IsStatusRegisterBitSet(bit_position) {  
152:       return isBitNSet(StatusRegister, bit_position);  
153:  }  
154:  /*************** TEST CODE *************/  
155:  function InitMemory () {  
156:       // 0000: start memory   
157:       mem8bit[0x0] = 0xa9;  
158:       mem8bit[0x1] = 0x0;  
159:       mem8bit[0x2] = 0x20;  
160:       mem8bit[0x3] = 0x10;  
161:       mem8bit[0x4] = 0x0;  
162:       mem8bit[0x5] = 0x4c;  
163:       mem8bit[0x6] = 0x02;  
164:       mem8bit[0x7] = 0x0;  
165:       mem8bit[0x8] = 0x0;  
166:       mem8bit[0x9] = 0x0;  
167:       mem8bit[0xa] = 0x0;  
168:       mem8bit[0xb] = 0x0;  
169:       mem8bit[0xc] = 0x0;  
170:       mem8bit[0xd] = 0x0;  
171:       mem8bit[0xe] = 0x0;  
172:       mem8bit[0xf] = 0x40;  
173:       //0010:  
174:       mem8bit[0x10] = 0xe8;  
175:       mem8bit[0x11] = 0x0;  
176:       mem8bit[0x12] = 0x20;  
177:       mem8bit[0x13] = 0x10;  
178:       mem8bit[0x14] = 0x0;  
179:       mem8bit[0x15] = 0x4c;  
180:       mem8bit[0x16] = 0x02;  
181:       mem8bit[0x17] = 0x0;  
182:       mem8bit[0x18] = 0x0;  
183:       mem8bit[0x19] = 0x0;  
184:       mem8bit[0x1a] = 0x0;  
185:       mem8bit[0x1b] = 0x0;  
186:       mem8bit[0x1c] = 0x0;  
187:       mem8bit[0x1d] = 0x0;  
188:       mem8bit[0x1e] = 0x0;  
189:       mem8bit[0x1f] = 0x40;  
190:  }  
191:  function memdump() {  
192:       var memdumpStr = "0000: ";  
193:       var memvalue = "";  
194:       for (memindex = 0; memindex < 32; memindex++) {  
195:            memvalue = mem8bit[memindex].toString(16);  
196:            if (memvalue == "0"){  
197:                 memvalue = "00"  
198:            }  
199:            memdumpStr = memdumpStr + " " + memvalue;  
200:       }  
201:       return memdumpStr;  
202:  }  

Monday, June 24, 2013

HTML5 Blitting Using an Offscreen Buffer



One of the great benefits HTML5 has brought us javascript game hackers is the canvas and context. Or more important, the ability to create an off screen canvas and context. This brings us one step closer to game console animation. Video games use off screen buffers for rendering. This technique provides smoother animation to the video game. The process involves rendering all the video game updates to an off screen buffer. An off screen buffer means that all the animation is being written to a buffer that is not visible.  When the game code has completed the off screen rendering, the buffer is FLIPPED to the visible video memory buffer.  Your game console and pc has hardware that handles this in video hardware. We're going to emulate this functionality using software. The reason buffer flipping is used is because it produces smoother looking animation in your video game.
This javascript tutorial is going to draw a simple background and a sprite using off screen buffer rendering and flipping. I am going to keep this simple. In the next demo we'll make the sprite walk and use multiple frames of animation. I just wanted to "keep it simple stupid" so you could get the idea of what is going on in the code. 

The Game Console Class

Over time I have been revamping the original game console code so I can use it in a library to write games. The first objective in this endeavor is to create a Game Console class, js/GameConsoleClass.js.

Source Listing 1. Defining a Game Console class in Javascript

function GameConsole(width, height) {
    this.width = width;
    this.height = height;  
    
    // create the main canvas
    this.canvas = document.createElement( 'canvas' );  
    this.canvas.width = this.width;
    this.canvas.height = this.height;
    this.context = this.canvas.getContext( '2d' );
    
    // add the canvas to the DOM
    document.body.appendChild(this.canvas);
    
    // create the offscreen buffer (canvas)
    this.offscreenCanvas = document.createElement('canvas');
    this.offscreenCanvas.width = this.width;
    this.offscreenCanvas.height = this.height;
    this.offscreenCanvas.context = this.offscreenCanvas.getContext('2d');

} // END GameConsole class


Of course we're going to need some member functions to perform the initialization and rendering.

Source Listing 2. Game Console member functions in Javascript

GameConsole.prototype = {

    initGC: function() {
        this.context.fillStyle = '#000000'; // Black
        //this.context.fillStyle = '#f0eef9'; // Lavender
        this.context.clearRect(0, 0, this.width, this.height);
        this.context.beginPath();
        this.context.rect(0, 0, this.width, this.height);
        this.context.closePath();
        this.context.fill();
    },   // END initGC
    
    // render to the offscreen canvas
    renderOffscreenGC: function(image) {
     // render the background
        this.offscreenCanvas.context.drawImage(image, 0, 0);
    
    }, // END render 
    
    bitBlitOffscreenGC: function(sprite, srcx, srcy, srcwidth, srcheight, dstx, dsty, dstwidth, dstheight) {
        
        this.offscreenCanvas.context.drawImage(sprite, srcx, srcy, srcwidth, srcheight, dstx, dsty, dstwidth, dstheight);
        
    },
    
    // flip the offscreen canvas to the visible GC canvas
    flipGC: function () {
        this.context.drawImage(this.offscreenCanvas, 0, 0);
    }, // END flipGC
    
}; // END GameConsole.prototype

Again, I kept everything really simplistic to give you a good idea of how the code works. I too have spent time in javascript books where the author's intent isn't to educate but to demonstrate how great their coding skills are. What is going on in their code is not blatantly obvious to the casual observer.

The Main Driver Code

Ok, let's draw some sprites. The source code js/MainGame.js contains the demo code to create a Game Console object in javascript and render to it. The code is logically straight forward with no sneaky coding demos. The demo code loads a background image and a sprite. That's it. Nothing fancy.



Source Listing 3. Loading and Blitting Sprites in Javascript

// create a Game Console object
var gc = new GameConsole(592,448);
gc.initGC();

// Background image
// load the image
var backgroundReady = false;
var backgroundImage = new Image();
backgroundImage.onload = function () {
    backgroundReady = true;
};
backgroundImage.src = "img/Level_1_warehouse.png";


// load the sprite
// load Sprite Sheet
var sprite = {};
var spriteSheetReady = false;
var spriteSheetImage = new Image();
spriteSheetImage.onload = function () {
    spriteSheetReady = true;
};
spriteSheetImage.src = "img/aliensvspredator_ltlinnkurosawa_sheet_sclx2.png";
var spriteWidth = spriteSheetImage.width /4;
var spriteHeight = 118;

gc.renderOffscreenGC(backgroundImage);

sprite.x = 250;
sprite.y = 250;

gc.bitBlitOffscreenGC(spriteSheetImage, 0, 144, spriteWidth, spriteHeight, sprite.x, sprite.y, spriteWidth, spriteHeight); 
gc.flipGC();

The code can be downloaded from GitHub   https://github.com/retrogamecode/tutorials.
Unfortunately, I can't upload the images there. So I will add them at the end of this article where you can grab it and download it.

Walking through the not so obvious code....

var sprite = {}; 


Creates a simple javascript object I will use for storing values for the sprite's current x (sprite.x), and y, (sprite.y) position. 

gc.renderOffscreenGC(backgroundImage);

sprite.x = 250;
sprite.y = 250;

gc.bitBlitOffscreenGC(spriteSheetImage, 0, 144, spriteWidth, spriteHeight, sprite.x, sprite.y, spriteWidth, spriteHeight); 
gc.flipGC();

In the bitBlit operation, the sprite.x and sprite.y are the destination positions I will draw the sprite. The x=0, and y =144 values for the bitBlit are values I pulled from photoshop when examining the sprite's location. It's a quick a dirty demo.

Since this is retro game coding we are blitting!!!! A bit blit is bit block transfer. We're kicking it old school here. Even though most low end PCs now have far more computing power than the CRAY computer of the early 90s. Anyhow, video game consoles of ages since past had hardware blitters in them for this operation. We're doing it in our software.

When the blit is completed -we FLIP the offscreen buffer to the visible buffer with the class method,

gc.flipGC();


Play around with the code and the sprites.


Source Listing 4. The HTML source file index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>RetroGameCode</title>
    </head>
    <h1>Game Console Offscreen Tutorial</h1>
        
        <script src="js/GameConsoleClass.js"></script>
        <script src="js/MainGame.js"></script>
    <body>
</html>

That's it for this demo. Have fun coding! Next venture we will move the sprites and do cell animation!



img/aliensvspredator_ltlinnkurosawa_sheet_sclx2.png

img/Level_1_warehouse.png

Tuesday, March 19, 2013

Creating a Console Game Screen Using HTML5

 After some time off from playing with this code from tutorials 1-3, I decided to go back and optimize and clean up a few things before we move on. This edition of retrogamecode we're going to look at creating a dynamic game console interface using HTML 5 canvas object.

I ripped out a lot of code for the examples to stand on their own and be clear to you the student or the developer.

Here are the basic javascript code snippets for creating a game console using the HTML5 canvas object.

You can download the entire HTML source file from github: create_html5_canvas.html

Source Listing 1. Defining a Super Nintendo Game Console in Javascript

// game console variables
var gc_canvas;                    // 'gc' 'game console'
var gc_context;                   // gc context
var gc_width = 592, gc_height = 448;   // game console viewport width and height Super Nintendo parameters
var gc_light_lavender = '#f0eef9';  // color light lavender
var gc_lavender = '#f0e7f9';   // color lavender
var gc_black = '#000000';    // color black

We're creating a Super Nintendo console of 592 pixels wide by 448 pixels high. The var gc_canvas is our game console HTML5 canvas object. Let's take a look at the javascript function we'll use to do this.

Source Listing 2. Dynamically creating an HTML5 canvas object in Javascript

// function: initGameConsoleCanvas
// description:
//    initialize the canvas element
//    and the drawing context API 
//    components.
//
function initGameConsoleCanvas( width, height) {
    
    // create the HTML5 canvas object
    gc_canvas = document.createElement( 'canvas' );   
    gc_context = gc_canvas.getContext( '2d' );
    
    // attach the canvas to the HTML document object
    document.body.appendChild( gc_canvas );
    gc_canvas.width = width;
    gc_canvas.height = height;
    
}
// END initGameConsoleCanvas(width, height)

We're going to need to a function to clear the screen for animation. And for right now we just want to throw something out on the screen to see if our game console canvas is working. The following function erases the game console canvas and fills the screen black.


Source Listing 3. Erase the HTML5 canvas object using Javascript

// function: clearGameConsoleCanvas
// description:
//    erase the game console using the 
//    specified color.
//
function clearGameConsoleCanvas(width, height, color) {
    // Fill the screen with a black background
   gc_context.fillStyle = color; //'#000000';
   gc_context.clearRect(0, 0, width, height);
   gc_context.beginPath();
   gc_context.rect(0, 0, width, height);
   gc_context.closePath();
   gc_context.fill();
}
// END clearGameConsoleCanvas(width, height, color)

Putting it all together, we call the document.onload() function to initialize the game screen as the web page initializes.

Source Listing 3. Erase the HTML5 canvas object using Javascript
// initialize on document load
window.onload = function() {

  // create the HTML 5 game console canvas 
  initGameConsoleCanvas(gc_width, gc_height);
  
  // initialize the game console canvas to black
  clearGameConsoleCanvas(gc_width, gc_height, gc_black);  
        
}
// END window.onload()

That's basically it. I hope by stripping out all the code the initialization of the game console canvas makes perfect sense to you. Ideally, all the game console code will be moved out to a javascript source file and then sourced in from the HTML file. We will do this in the next tutorial as we move along.

Have fun scripting! Go make some games.

Sunday, June 10, 2012

Part 02 Scrolling Background Bitmap with HTML 5

In tutorial 01, we learned a few basics about HTML 5. It has a canvas element and javascript has a context API we use to draw to the canvas. We're going to build upon the simple script from tutorial 01 and have the background scroll. Our offscreen background bitmap, Level_1_warehouse.png, is 977 pixels wide by 478 pixels high. Our game console viewport is 592 pixels wide and 448 pixels high. The background bitmap file is wider and taller than our viewport.

The object is to drag the viewport along the bitmap to give a moving background effect. We're keeping this example to a simple bitmap scroll. When you get the hang of this your renderGamConsole() function we wrote in tutorial 01 can be modified to draw sprites on multiple layers with different sprite velocities. This method rendering is called parallax scrolling. You can render clouds or a star field on the first plane of animation to giving a motion feel. Then draw a slower scrolling city scape in the background as you have a spaceship battle aliens. This method of drawing sprites and backgrounds on multiple layers is called parallax scrolling. For now we're going to focus on a simple background scroll. You can see in our screen shot from the Capcom game, Alien vs. Predators, that we have the background drawn first, then the Lt. Linn sprite and the alien boss, then the sprite health bars are drawn. There are about 3 layers of animation is the screen grab. So when we render this we will draw the background  layer first, the sprite layer(s) next, and then the status bar sprites last so they are foremost in the screen. Giving the feel of depth in the final rendering.

Code Cleanup

If you have been following along from tutorial 01 you'll notice I did a quick code cleanup. I separated functions and moved around some of the logic so it's easier to understand as we work on tutorial 02. There are basically only two new function calls in this code, checkgameConsoleInputDevice() and updatePlayer(). I added these two functions so we can manually control the bitmap scrolling using the input from a keyboard with the right and left arrow keys. I am using the jQuery library for keyboard input. This can actually change as I experiment and find which library works best with input to mobile devices and computers as well. For now we're just up and running with jQuery.

The keyboard code using jQuery is here.

Source Listing 1. Checking the game console input devices.

// function: checkGameConsoleInputDevice
// description:
//    jQuery routine to check the keyboard 
//    input.
//
function checkGameConsoleInputDevice() {


    $(document).keydown(function(evt) {
        if (evt.keyCode == 39) {
            if (bgx < 380) {
            gamePadMove = 'RIGHT';
            //bgx++;
            }
        } else if (evt.keyCode == 37){
            if (bgx > 0) {
            gamePadMove = 'LEFT';
            //bgx--;
            }
        }
    });         

    $(document).keyup(function(evt) {
        if (evt.keyCode == 39) {
            gamePadMove = 'NONE';
        } else if (evt.keyCode == 37){
            gamePadMove = 'NONE';
        }
    }); 
}
// END checkGameConsoleInputDevice


As you can see, the checkGameConsole() function is the basics for running a game pad or controller. We don't care if it is arrow icons on a mobile device or right and left arrows on a keyboard. This is the skeleton of our final function for checking the input values on our game console. I am setting flags if the game pad was pressed or in this simple case if the right or left arrow was pressed. These values are passed on to the game for updating the values in the game.

In function updatePlayer(), we listen for the gamePadMove flags to be set. When we act on the flag needing update, we then reset the flag.

Source Listing 2. Updating the gmae position
// function: updatePlayer
// description:
//    based on the input from
//    checkGameConsoleInputDevice
//    update the player position.
//
function updatePlayer() {

    if (gamePadMove == 'RIGHT') {
      bgx += 5;
      gamePadMove = 'NONE';
    }  
    if (gamePadMove == 'LEFT') {
      bgx -= 5;
      gamePadMove = 'NONE';
    }  
  // end switch gamePadMove
  
}
// END updatePlayer

The bgx variable is a global variable used to identify the current x-position of the origin x-component of the viewport. So as the viewport slides back and forth over the background we update the bgx variable. Essentially, this is the bottom-left corner of the viewport rectangle as slide across the bitmap.

Scrolling Background

Now we get to the topic of interest, scrolling the background. We are going to scroll the background to the right or to the left based on user input. Now that our background is non-static and it moves, we need to update how we render the bitmap in renderGameConsole() function.


Source Listing 3. Changes for scrolling bitmaps.
// function: renderGameConsole
// description:
//   draws the content to the 
//   game console canvas.
//
function renderGameConsole() {
  
  // gc_context.drawImage(img_elem, srcx, srcy, srcwidth, srcheight, dx, dy, dw, dh);
  // draw the background first
  try {
    gc_context.drawImage(background, bgx, bgy, width, height, 0, 0, gc_canvas.width, gc_canvas.height);
  } 
    catch (e) {
  };
    
}
// END renderGameConsole


Running the Code in Your Sandbox

You'll need to copy the code below and paste it into an editor. Save the file as tutorial_02.html. Your development environment should be a simple directory, with one subdirectory, called img. Place the background art file into this directory. Your setup should look like this.

The entire source code for this example is presented here. We're using the same art file from tutorial 01. You can download the Level_1_warehouse.png art file here.

Source Listing 4. The whole tamale!


<html>
<title>retrogamecode tutorial 02 scrolling background</title>
<head>

</head>
<body>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

<script type="text/javascript">
// requestAnim shim layer by Paul Irish
    window.requestAnimFrame = (function(){
      return  window.requestAnimationFrame       || 
              window.webkitRequestAnimationFrame || 
              window.mozRequestAnimationFrame    || 
              window.oRequestAnimationFrame      || 
              window.msRequestAnimationFrame     || 
              function(/* function */ callback, /* DOMElement */ element){
                window.setTimeout(callback, 1000 / 60);
              };
    })();
// example code from mr doob : http://mrdoob.com/lab/javascript/requestanimationframe/

// http://www.retrogamecode.blogspot.com
// author: michael norton


//------------------------------------------
// Global Variables Declarations
//------------------------------------------

var gc_canvas;                  // 'gc' 'game console'
var gc_context;                 // context
var width = 592, height = 448;  // game console viewport width and height
var background;                 // image asset background
var bgwidth, bgheight;          // background width and height
var bgx, bgy;                   // background source x and y used for blitting

var gamePadMove;                // game console device input

//------------------------------------------
// Functions
//------------------------------------------

// Game Console Functions

// function: initGameConsoleCanvas
// description:
//    initialize the canvas element
//    and the drawing context API 
//    components.
//
function initGameConsoleCanvas() {
    
    gc_canvas = document.createElement( 'canvas' );
    gc_context = gc_canvas.getContext( '2d' );
    gc_canvas.width = width;
    gc_canvas.height = height;
    document.body.appendChild( gc_canvas );
}
// END init

// function: clearGameConsole
// description:
//   clears the game console before each
//   call to renderGameConsole.
//
function clearGameConsole(){
  gc_context.fillStyle = '#f0e7f9';
  gc_context.clearRect(0, 0, width, height);
  gc_context.beginPath();
  gc_context.rect(0, 0, width, height);
  gc_context.closePath();
  gc_context.fill();
}
// END clearGameConsole

// function: renderGameConsole
// description:
//   draws the content to the 
//   game console canvas.
//
function renderGameConsole() {
  
  // gc_context.drawImage(img_elem, srcx, srcy, srcwidth, srcheight, dx, dy, dw, dh);
  // draw the background first
  try {
    gc_context.drawImage(background, bgx, bgy, width, height, 0, 0, gc_canvas.width, gc_canvas.height);
  } 
    catch (e) {
  };
    
}
// END renderGameConsole

// function: checkGameConsoleInputDevice
// description:
//    jQuery routine to check the keyboard 
//    input.
//
function checkGameConsoleInputDevice() {


    $(document).keydown(function(evt) {
        if (evt.keyCode == 39) {
            if (bgx < 380) {
            gamePadMove = 'RIGHT';
            //bgx++;
            }
        } else if (evt.keyCode == 37){
            if (bgx > 0) {
            gamePadMove = 'LEFT';
            //bgx--;
            }
        }
    });         

    $(document).keyup(function(evt) {
        if (evt.keyCode == 39) {
            gamePadMove = 'NONE';
        } else if (evt.keyCode == 37){
            gamePadMove = 'NONE';
        }
    }); 
}
// END checkGameConsoleInputDevice

// Game specific functions

// function: loadImageAssets
// description:
//    loads the background art file
//    from local disk.
function loadImageAssets(){

  background = new Image()
  background.src = "file:img/Level_1_warehouse.png"
  
  // background pixel dimensions
  bgwidth = 977;
  bgheight = 478;
  
  // initialize background source blitting x and y
  bgx = 0;
  bgy = 0;
}
// END loadImageAssets 

// function: updatePlayer
// description:
//    based on the input from
//    checkGameConsoleInputDevice
//    update the player position.
//
function updatePlayer() {

    if (gamePadMove == 'RIGHT') {
      bgx += 5;
      gamePadMove = 'NONE';
    }  
    if (gamePadMove == 'LEFT') {
      bgx -= 5;
      gamePadMove = 'NONE';
    }  
  // end switch gamePadMove
  
}
// END updatePlayer

// function: animate
// description:
//   this is the main game lopp
//   function. It calls itself 
//   recursively.
function animate() {
    requestAnimFrame( animate );

    updatePlayer();
    clearGameConsole();
    renderGameConsole();
    checkGameConsoleInputDevice();
}
// END animate



// main game loop
initGameConsoleCanvas();
loadImageAssets();
animate();

</script>
</body>
</html>

Wednesday, June 6, 2012

Part I Drawing Sprites to an HTML5 Canvas



In my days of old I started off as an assembly language and machine code programmer. I spent my days looking at hardware register values and pointers to jump tables in memory. I had worked at Activision video games, which back then sported a benign meaningless name, Mediagenics Activision. I worked there with another engineer on reverse engineering a SEGA Genesis. Why you ask? We were looking for a cheaper means for developers to hack into the SEGA. The development systems were about $50K a pop back then and we were on a quest to hardware hack a cheaper development system. As we messed with SEGA Genesis hardware and machine code we would sometimes joke that one day in the future we would be scripting games and not machine coding them. Nonsense! We would chuckle at the mere possibility of such a crazy notion. That was an adorable fairy tale. Well, I guess the future is now. Here we are -scripting video games.

The SNES, SEGA Genesis, and the Nintendo N64 are in my blood as a game hacking hobbyist. I love back engineering games. Sure we all have a copy of MAME and cough as we lie about owning the actual roms. I own MAME for educational and hobby purposes. I am the guy that can't leave the toaster alone and has to disassemble it to see how it works. And then rebuild it with improvements. Video games to me are no different. In writing this I could careless if you wrote a complete game engine. I have a life and I am not going to sit down and look through your 10,000 lines of spaghetti code to realize I should have wrote mine from scratch in the first place. What a headache! This blog is all about sitting in front of your fireplace, with your favorite hacking platform of choice, throwing on your favorite smoking jacket and banging out some HTML 5 javascript code.

Today I am going to focus my attention on that mid-90's master piece, the coin op video game, Aliens vs. Predator! This ultimate arcade aliens versus Colonial Marines smack down game was released by Capcom. Sure you could play as a predator too, but the character I loved to throw a beating to the aliens with was Lieutenant Linn Kurosawa. Back when this game was out as a coin op I would shovel a few quarters into the machine at the Milpitas Pizza Depot as I waited for my pizza to cook. Lt. Linn was my favorite character because she was the one player wielding a katana and wielding it to take out alien hives. I remember making an early attempt at writing this game in C on my first gen Intel Pentium powered machine. But I wasn't much of a computer artist so my initial game had frustrating results in the sprite department. Now with MAME, screen grab tools, online sprite sheets, and that wonderful expensive application -Photoshop. It's back to hacking Capcom's coin op Alien vs. Predator.

The code demonstration here is all going to be straight forward. I get sick at looking at tutorials and you have to unravel the clever code the grad student used. This code is all for edification to help you learn HTML 5's Canvas and the javascript methods used to access it.

In this tutorial we're going to load an offscreen bitmap, or background onto a static screen. Don't worry, in the next tutorial we're going to scroll the background bitmap. Just like they do in the arcade games. For this example, I created a background from the second round of level 1 in the Aliens vs. Predator game. I did multiple screen grabs and pasted together a full background for us to use. You must download the art on this site to a local directory on your development machine. For right now all the work we're doing is off your local development machine. For this tutorial all the assets for the game will be in a local directory: img.

Download the background here:  Level_1_warehouse.png
Our background image is 977 pixels wide x 478 pixels high.











A Quick Blurb on Game Loops

Whenever we write games we need a main loop. That is a function that loops forever until the game is requested to stop by the user. Game loops have come a long way since the 90's. The days of old we simply wrote some code which looked like,

while(!done) {
   updateGame();
}

That was the beauty of machine code and raw C on DOS based PCs and game consoles of days of old. Not anymore! Now most applications are all window driven to some extent. A browser is a windowed application running on a windowed  operating system such as, OS X or Windows or whatever. These environments listen for events and we need an event listener. After many wrong ways of coding my game loop I decided to just go shopping with the Google search engine and find an event handler that would work with HTML 5. I felt I would mention this fact and not just dump code on you that any grad student would surmise to be intuitively obvious to the casual observer. I am using an animate frame listener. I found one such gem on Mr. Doob's website, which also functions when the window is not active and stops the graphics animation. A plus for mobile devices applications. Here is the basic code and HTML syntax for the start of this project.

Source Listing 1. Basic HTML with requestAnimationFrame callback
<html>
<head>
</head>
<body>

<script>
// requestAnim shim layer by Paul Irish
    window.requestAnimFrame = (function(){
      return  window.requestAnimationFrame       || 
              window.webkitRequestAnimationFrame || 
              window.mozRequestAnimationFrame    || 
              window.oRequestAnimationFrame      || 
              window.msRequestAnimationFrame     || 
              function(/* function */ callback, /* DOMElement */ element){
                window.setTimeout(callback, 1000 / 60);
              };
    })();
  

// example code from mr doob : http://mrdoob.com/lab/javascript/requestanimationframe/

</script>
</body>
</html>

That's it for our basic requirements to implement our game loop. Let's move on and setup the HTML 5 canvas.

HTML 5 Canvas and Context

The first thing we need to do is setup a simple HTML file for our javascript to reside in. You'll notice off the bat I don't have CSS in the source. It's not because I can't do CSS in my sleep it's because it's a distraction to those blokes trying to learn HTML 5 and javascript. When the time is right we'll add the CSS. Right now, just get comfortable with what is going on and what we're doing.

In side our HTML script element tag we're going to add the following code.

Source Listing 2. Global variables and initializing the game console viewport
// global variables
var gc_canvas;                  // 'gc' 'game console'
var gc_context;                 // context
var width = 592, height = 448;  // game console viewport width and height
var background;                 // image asset background
var bgwidth, bgheight;          // background width and height
var bgx, bgy;                   // background source x and y used for blitting

function initGameConsoleCanvas() {
    
    gc_canvas = document.createElement( 'canvas' );
    gc_context = gc_canvas.getContext( '2d' );
    gc_canvas.width = width;
    gc_canvas.height = height;
    document.body.appendChild( gc_canvas );
}
// END initGameConsole

 We have global variables for the browser <canvas> element, labelled gc_canvas, for game console canvas. And we have canvas drawing context variable, gc_context. The canvas element is the surface we draw to. The context is the javascript API we which we draw to the canvas with. These are two distinct components here. The canvas is part of the HTML 5 component, and the context is the mechanism (API) in javascript which we draw to the canvas element with.
In the code, we are creating a game console that is 592 pixels wide by 448 pixels wide. Why these dimensions? They were determined by analyzing the size of the screen grabs from the MAME application. The arcade resolution is 592 x 448 pixels. If you would like to create a SNES console, change the dimensions to 512 x 478, and so forth.  The remaining global variables are for loading and blitting the background bitmap.

Clear the Game Console Viewport

We're going to need a simple screen refresh that clears the screen. Basically, we're going to do a simple color fill. I use a sky blue, just so I know something is happening when I am debugging the code.

Source Listing 3. Clearing the Game Console Screen
function clearGameConsole(){
  gc_context.fillStyle = '#d0e7f9';
  gc_context.clearRect(0, 0, width, height);
  gc_context.beginPath();
  gc_context.rect(0, 0, width, height);
  gc_context.closePath();
  gc_context.fill();
}

During each iteration of our animationFrame callback we will call function clearGameConsole. This will essentially flood fill the viewport that is 592 x 448 pixels with the color sky blue or #d0e7f9. This is our basic clear screen before we render the background with each animation iteration.

Load the Asset

Now we need a function to load our bitmap from the local system to the application on the browser. At the moment we are only loading from local disk. In a future tutorial we will work with loading assets off the Internet which will require some more coding and a fancy load status bar. For now, we just want to throw out our bitmap onto the canvas of the browser. Remember, we have our tutorial_01.html file stored in a directory that has a subdirectory named img. In directory, img, we have the asset or bitmap file, Level_1_warehouse.png. With this in mind. let's look at the code to load the asset.

Source Listing 4. Load the local file asset.
function loadImageAssets(){

  background = new Image()
  background.src = "file:img/Level_1_warehouse.png"
  
  // background pixel dimensions
  bgwidth = 977;
  bgheight = 478;
  
  // initialize background source blitting x and y
  bgx = 0;
  bgy = 0;
}
// END loadImageAssets 


Rendering to the Game Console canvas

Now we need code to blit our bitmap, which is not stored in a javascript Image object. This is a simple render function that only draws a static image to the canvas element. As we expand our code in future tutorials we will discuss levels of blitting. This is important for parallax scrolling and scrolling bitmaps with various depths of sprite activity going on in the game scene. For now we have a simple blit operation to just through a portion of the bitmap out to the game console viewport (the canvas). Remember, our offscreen bitmap is 977 x 478 pixels. It is much larger than our game console viewport, which is 592 x 448 pixels. We will only blit a portion of bitmap to the viewport.

Source Listing 5. Blitting the bitmap to the game console viewport.
function renderGameConsole() {

  
  // gc_context.drawImage(img_elem, srcx, srcy, srcwidth, srcheight, dx, dy, dw, dh);
  // draw the background first
  try {
    gc_context.drawImage(background, bgx, bgy, gc_canvas.width, gc_canvas.height, 0, 0, gc_canvas.width, gc_canvas.height);
  } 
    catch (e) {
  };
    
}
// END renderGameConsole

We use the context API drawImage to blit to the game console viewport.

Source Listing 6. The animation loop
function animate() {
    requestAnimFrame( animate );

    clearGameConsole();
    renderGameConsole();
}
// END animate

We recursively call requestAnimFrame with our animate function as a parameter. This is the magic woobly, goobly, goo that makes time and relative dimension in space travel and coding possible. That in a nutshell is our game animation loop.
That's the whole lesson for right now. Next time we see you we'll be scrolling that background!
Remember, download the art to an img subdirectory,  should look like this.

tutorial_01.html
\img
    Level_01_warehouse.png

Or from a window environment, like this.


Just copy and paste the code below into your editor and save it as tutorial_01.html.

Source Listing 7. The whole tamale of code for tutorial_01.html !
 
<html>
<head>
</head>
<body>

<script>
// requestAnim shim layer by Paul Irish
    window.requestAnimFrame = (function(){
      return  window.requestAnimationFrame       || 
              window.webkitRequestAnimationFrame || 
              window.mozRequestAnimationFrame    || 
              window.oRequestAnimationFrame      || 
              window.msRequestAnimationFrame     || 
              function(/* function */ callback, /* DOMElement */ element){
                window.setTimeout(callback, 1000 / 60);
              };
    })();
  

// example code from mr doob : http://mrdoob.com/lab/javascript/requestanimationframe/

// global variables
var gc_canvas;                  // 'gc' 'game console'
var gc_context;                 // context
var width = 592, height = 448;  // game console viewport width and height
var background;                 // image asset background
var bgwidth, bgheight;          // background width and height
var bgx, bgy;                   // background source x and y used for blitting

// main code section
initGameConsoleCanvas();
loadImageAssets();
animate();

// function library
function initGameConsoleCanvas() {
    
    gc_canvas = document.createElement( 'canvas' );
    gc_context = gc_canvas.getContext( '2d' );
    gc_canvas.width = width;
    gc_canvas.height = height;
    document.body.appendChild( gc_canvas );
}
// END initGameConsole

function clearGameConsole(){
  gc_context.fillStyle = '#f0e7f9';
  gc_context.clearRect(0, 0, width, height);
  gc_context.beginPath();
  gc_context.rect(0, 0, width, height);
  gc_context.closePath();
  gc_context.fill();
}

function loadImageAssets(){

  background = new Image()
  background.src = "file:img/Level_1_warehouse.png"
  
  // background pixel dimensions
  bgwidth = 977;
  bgheight = 478;
  
  // initialize background source blitting x and y
  bgx = 0;
  bgy = 0;
}
// END loadImageAssets 


function animate() {
    requestAnimFrame( animate );

    clearGameConsole();
    renderGameConsole();
}
// END animate

function renderGameConsole() {

  
  // gc_context.drawImage(img_elem, srcx, srcy, srcwidth, srcheight, dx, dy, dw, dh);
  // draw the background first
  try {
    gc_context.drawImage(background, bgx, bgy, gc_canvas.width, gc_canvas.height, 0, 0, gc_canvas.width, gc_canvas.height);
  } 
    catch (e) {
  };
    
}
// END renderGameConsole 
</script>
</body>
</html>





Wednesday, October 5, 2011

Retro 3D Pipeline

Nowadays we have OpenGL to handle all our 3D needs. But I like kickin it old school and taking things apart. When I was learning computer graphics in the 80s we were coding everything from scratch or from text books. 3D libraries weren't readily available and the WWW just wasn't born yet.

In this article I am going to blend javascript with the HTML 5 canvas and do some old school 3-D'in! I am not going to bore you with a lot of details I just want to provide for you a simple demonstration of 3D rotation in a text book purists sense. That mean's easy to follow code for you to cut and paste into your own coding experiments. Easy to follow means non-optimized or anything clever. Just straight up 3D!

While we're kickin it old school I am going to create a sin and cos lookup table. After all we're using a scripting language and not any flavor of a compiled language like, C, C++, C#, or Objective-C. This is so 1990's but has it's applications here, the trig lookup tables.

 
var sin_table = new Array();
var cos_table = new Array();

// function: initialize_trig_tables
//
// description: initialize the trig
// sin and cos lookup tables
//
// arguments: none
// returns: none
function initialize_trig_tables ()
{

for(angle=0; angle<361; angle++)
{
sin_table[angle] = Math.sin(angle);
cos_table[angle] = Math.cos(angle);
}
}


We're not done with our initializations yet. We will need functions to create a null matrix and an identity matrix.
 

// function: makeIdentityMatrix
//
// description: create a 4x4 identity matrix
//
// 1 0 0 0
// I = 0 1 0 0
// 0 0 1 0
// 0 0 0 1
//
// arguments: none
// returns: 4x4 identity matrix
function makeIdentityMatrix ()
{

return [
[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1],
];
}

// function: makeNullMatrix
//
// description: create a 4x4 identity matrix
//
// 0 0 0 0
// N = 0 0 0 0
// 0 0 0 0
// 0 0 0 0
//
// arguments: none
// returns: 4x4 NULL matrix
function makeNullMatrix ()
{
return [
[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
];

}


Now we need some code to perform the rotations Rx, Ry, and Rz. These are the respective rotations about the axis of x,y, and z. I store the rotation and projection code in a javascript library. Just a heads up for the driver code where it's implied you have this in a library somewhere.



For rotation Rx,

 
// function: createRotationXMatrix
//
// Description: creates a rotation matrix
// about the x-axis
//
// 1 0 0 0
// Rx(a) = 0 cos a -sin a 0
// 0 sin a cos a 0
// 0 0 0 1
//
// arguments: angle - angle of rotation
// returns: 4x4 Rotation matrix Rx
function createRotationXMatrix (angle)
{
// create and initalize rx to identity matrix
var rx = makeIdentityMatrix ();

rx[1][1] = cos_table[angle];
rx[1][2] = sin_table[angle];
rx[2][1] = -1 * sin_table[angle];
rx[2][2] = cos_table[angle];

return rx;
}


For rotation Ry,

 
// function: createRotationYMatrix
//
// Description: creates a rotation matrix
// about the y-axis
//
// cos t 0 sin t 0
// Ry(t) = 0 1 0 0
// -sin t 0 cos t 0
// 0 0 0 1
//
// arguments: angle - angle of rotation
// returns: 4x4 Rotation matrix Ry
function createRotationYMatrix (angle)
{
// create and initalize rx to identity matrix
var ry = makeIdentityMatrix ();

ry[0][0] = cos_table[angle];
ry[0][2] = -1 * sin_table[angle];
ry[2][0] = sin_table[angle];
ry[2][2] = cos_table[angle];

return ry;
}



For rotation about the Z axis, Rz,

 
// function: createRotationZMatrix
//
// Description: creates a rotation matrix
// about the z-axis
//
// cos p -sin p 0 0
// Rz(p) = sin p cos p 0 0
// 0 0 1 0
// 0 0 0 1
//
// arguments: angle - angle of rotation
// returns: 4x4 Rotation matrix Rz
function createRotationZMatrix (angle)
{
// create and initalize rz to identity matrix
var rz = makeIdentityMatrix ();

rz[0][0] = cos_table[angle];
rz[0][1] = -1 * sin_table[angle];
rz[1][0] = sin_table[angle];
rz[1][1] = cos_table[angle];

return rz;
}


Now we need a function to multiply matrices. Simple and straight forward -no optimization. This looks like,

 
// function: matrixMultiply
//
// Description: multiplies 2
// 4x4 matrices and returns the result
// arguments: A - matrix, B - matrix
// returns: 4x4 Matrix C as result of A * B
function matrixMultiply ( A, B ) {

// initialize an empty 4x4 matrix
var C = makeNullMatrix();

// Cij = E Aik Bkj
for (i=0; i<4;i++)
{
for (j=0; j<4; j++)
{
for (k=0; k<4; k++)
C[i][j] = C[i][j] + A[i][k]*B[k][j]
}
}
return C;
}


Using function matrixMultiply, we crunch some numbers brute force and create the Rotation desired,

 
// function: createRotationRMatrix
//
function createRotationRMatrix (anglex, angley, anglez)
{

var Rx = createRotationXMatrix (anglex);
var Ry = createRotationYMatrix (angley);
var Rz = createRotationXMatrix (anglez);

return ( matrixMultiply ( matrixMultiply ( Rx, Ry ), Rz ) );
}


The function returns a matrix with our desired results.

What is required now is means to project the rotation onto the display. We now need a perspective projection.

 
// Perspective Projection
//
// | 1 0 0 0 |
// | 0 1 0 0 |
// [x y z 1] | 0 0 0 1/d|
// | 0 0 0 1 |
//
function gc_projection ( x,y,z,d,P)
{
var xy_Array = new Array();

var gc_width = 800; // game console context width
var gc_height = 600; // game console context height

var xp = P[0][0]*x+P[1][0]*y+P[2][0]*z;
var yp = P[0][1]*x+P[1][1]*y+P[2][1]*z;
var zp = P[0][2]*x+P[1][2]*y+P[2][2]*z + 10;
var aspect_ratio = gc_width/gc_height;

xp = (gc_width/2) + (xp * d / zp);
yp = (gc_height/2) + (aspect_ratio * yp * d /zp);

xy_Array[0] = xp;
xy_Array[1] = yp;

return xy_Array;
}


Here's what a simple driver code looks like in javascript.

 


:
snip
:

var c=document.getElementById("myCanvas");
var cxt = c.getContext("2d");

var vertex = [
[0.57735026919, 0.57735026919, 0.57735026919],
[0.57735026919, -0.57735026919, -0.57735026919],
[-0.57735026919, -0.57735026919, 0.57735026919],
];

var P = createRotationRMatrix (30,30,30);
var vertex_list = new Array();

for (i=0; i<3; i++) { vertex_list[i] = gc_projection ( vertex[i][0],vertex[i][1],vertex[i][2],2000,P) } // render it cxt.moveTo(vertex_list[0][0],vertex_list[0][1]); cxt.lineTo(vertex_list[1][0],vertex_list[1][1]); cxt.lineTo(vertex_list[2][0],vertex_list[2][1]); cxt.lineTo(vertex_list[0][0],vertex_list[0][1]);


That's that!