One of my favorite games from when I was a kid was Squish ’Em, which I played on our Atari 130XE computer. In 2007 I decided it would be a fun game to port to the Atari 2600. It already looked like it had been designed with the 2600’s limitations in mind, so it might be possible to create an almost-exact port.
I ended up almost completely disassembling the original game in an effort to get as close to the original as possible and, with the exception of the music (which is about an octave higher in the original, notes that the 2600 can’t reach) it is virtually identical.
Another of my favorite games from my childhood was a type-in game called Elevator Repairman from Analog magazine. Thankfully, I didn’t have to type it in; I acquired it as part of a shareware compilation disk I bought through an Atari user group. In that game, you control an elevator repairman trying to reach the top of a building, dodging elevators. That game, in turn, was based on an older Atari computer game called Spy’s Demise. I took the same basic concept and rethemed it with, of all things, a maid theme. You control a maid trying to reach the top of a series of hotels while dodging out of control elevators.
I ended up doing the graphics for this one myself; pretty much the only graphic was the maid itself. I modified the old Atari computer font for the text display. I had my brother write music for this one: a main gameplay theme and a little ditty that plays when the poor maid gets hit by an elevator.
I also supported the AtariVox and SaveKey for saving high scores, which was an interesting technical challenge. Since accessing the SaveKey takes so long, in the past (for Go Fish!) I had blanked the screen completely for 1/60th of a second while I saved or updated high scores. I didn’t like that solution — I thought it looked unprofessional — so for Elevators Amiss I split the SaveKey routines up, spreading them over several frames, and continued to display an attract screen while updating high scores.
David Galloway had been working on this a little bit and he asked me to write the kernel, or display code, for this game: a 2600 version of the old LED football games from the late 70s and early 80s. I wrote two versions of the display code; one in black & white and a very flickery color version. David also wanted some appropriately bombastic title screen music for this, Copland’s Fanfare for the Common Man, so I asked Tommy to convert that to a two-channel 2600 version, which I coded up with the latest version of my music driver.
Zach Matley was writing a 2600 port of the old board game Connect Four, which he renamed Four-Play, both to avoid trademark issues as well as for the giggles it evoked. He asked me to write a title-screen song for him that somehow had a "four" theme. I played around with some different ideas and ended up writing a couple different 4-note themes that I wove together in sort of a call-and-response format.
This was based on my very first, overly ambitious 2600 programming project: A side-scrolling platformer. Due to the technical limitations of the Atari 2600, horizontal scrolling is both very difficult and very difficult to make look good. After abandoning the project for almost a year, I resurrected it, rethemed, as a Christmas game: Santa has lost his reindeer and must dash off over hill and dale to find them. Nathan Strum created all the graphics and helped a lot with playtesting and level design (and created all the graphics), and Tommy helped me write a 2600 version of Silent Night.
For the last 10 years or so there has been a "minigame" competition — mostly for 8-bit technology, the goal is to write the best game in the smallest space possible. In 2005 there were so many Atari 2600 entries that we thought it would be fun to put them all on a single cartridge and add a menu to select between them. My entry on the cart was a 4K game; a port/expansion of an old (1977!) coin-op game called M-4. In that old game, you controlled a tank on either the left or the right of the screen and fired missiles across the screen, trying to hit the other tank on the other side. My enhancements included: color, moving walls, a computer-controlled opponent (the original was 2-player only), and invisible tanks. I also wrote and programmed the music that plays while the cart's menu is displayed.
Also included on the cart was a 1K version of Go Fish!, hidden as an Easter Egg.
Self-directed, detail-oriented programmer with demonstrated desire and ability to learn new skills and technologies.
2000 - 2010: Research Data Analyst at NexCura, an Internet software company specializing in treatment decision information tools for chronic diseases.
- Evaluated, coordinated, and implemented application changes, working with clinical staff and developers to ensure a smooth and timely process
- Coordinated bi-monthly application updates
- Maintained data integrity across several large relational databases (SQL Server 2005)
- Aggregated data for reporting and projections from multi-million row tables using T-SQL and MS Access
- Maintained and updated Transact SQL code for a 1.5 million user online tool
- Wrote technical specifications for new products and enhancements to existing products
- Helped design and spec changes, additions, and updates to intra-office database interface
- Helped troubleshoot and fix bugs in SQL back-end of intra-office tools
- Summarized articles from medical journals and extracted relevant data
- Built email messages and managed ongoing email campaigns
- Put together campaign projections for specific geographical areas and customer targets
- Built online screeners and surveys
- Trained new Research Data Analysts and coordinated their workload
Published video games
- 2007, programming
- 2007, design, programming, and graphics
AtariAge Holiday Greetings 2006
- 2006, later re-released as Toyshop Trouble, design and programming of hidden "Easter Egg" game
- 2006, graphics and music programming
- 2006, music
AtariAge Holiday Greetings 2005
- 2005, later re-released as Reindeer Rescue, design, programming, and music
2005 Minigame Multicart
- 2005, design, programming, and graphics
- 2005, design, programming, and graphics
An examination of the influence of patient race and ethnicity on expressed interest in learning about cancer clinical trials, 134 J Cancer Res Clin Oncol. 115 (2008) (co-authored with Maurie Markman and Judy Petersen)
Cancer versus heart failure patient self-declared potential interest in clinical trials, 9 Curr Oncol Rep. 1 (2007) (co-authored with Maurie Markman, Larie Smoyer, and J. Timothy Marron)
Influence of tumor type, disease status, and patient age on self-reported interest regarding participation in cancer clinical trials, 107 Cancer. 849 (2006) (co-authored with Maurie Markman and Judy Petersen)
An examination of characteristics of lung and colon cancer patients participating in a web-based decision support program. Internet-based decision support programs, 69 Oncology. 311 (2005) (co-authored with Maurie Markman and Judy Petersen)
Bachelor of Science in Mathematics, minor in Computer Science, minor in Physics, 2000
Western Washington University, Bellingham, Washington
Playing soccer, the piano, and the guitar. Also enjoy designing and writing video games for classic consoles, and have composed and arranged music for several published Atari 2600 games.
When programming for the Atari 2600, graphics data needs to be in a specific format: generally you need all graphics in monochrome, 8-pixel wide columns, and those graphics must be converted to data statements where each byte represents 8 pixels. Additionally, you want all graphics to be laid out bottom to top (i.e., upside down) and usually, to make it a little easier to look at, you represent them in binary, with one byte per line; like this:
.byte %00111111 .byte %00001111 .byte %00000011 .byte %00000000 .byte %00000001 .byte %00000001 .byte %00000011
As you may imagine, creating those kind of data statements by hand for a large number of different graphic objects is slow, tedious, and prone to error. So I thought I'd write a console app, in C++, to do it automatically. This is still a bit of a work in progress, but currently it will read any bitmap with a width that is a multiple of eight, and create a file with each 8-pixel column converted to data statements. It takes the color of the top left pixel as background and all other colors as foreground.
Just for fun, I translated GfxTo2600 to a SQL stored procedure; here's the heart of the routine:
set @index1 = 0 while @index1 < @imageWidth / 8 --loop through all columns begin set @index2 = 0 while @index2 < @imageHeight --loop through all rows begin set @gfxLine = ' .byte %' set @index3 = 0 while @index3 < case when @bitsPerPixel >= 8 then 8 else @bitsPerPixel end begin --two routines: first for when > 1 pixel is packed into a byte, -- second for all other cases if @bitsPerPixel >= 8 begin set @pixelData = substring(@imageData, @dataPtr, @rowWidth/@imageWidth) if @bitsPerPixel = 8 begin set @pixelColor = cast(substring(@pixelData, 1, 1) as int) set @dataPtr = @dataPtr + 1 end else --24-bit pixels begin set @pixelColor = cast(@pixelData as int) set @dataPtr = @dataPtr + 3 end set @gfxLine = @gfxLine + case when @pixelColor = @backgroundColor then '0' else '1' end end else --routine for unpacking pixels from byte begin set @pixelData = substring(@imageData, @dataPtr, 1) set @index4 = 0 set @pixelColor = cast(@pixelData as int) while @index4 < 8 / @bitsPerPixel begin if @bitsPerPixel = 1 begin set @gfxLine = @gfxLine + case @pixelColor & 128 when @backgroundColor then '0' else '1' end --shift one bit to the left set @pixelColor = @pixelColor * 2 end else begin set @gfxLine = @gfxLine + case @pixelColor & 240 when @backgroundColor then '0' else '1' end --shift 4 bits to the left set @pixelColor = @pixelColor * 16 end set @index4 = @index4 + 1 end set @dataPtr = @dataPtr + 1 end set @index3 = @index3 + 1 end --insert row into results table insert into #gfxData (ColumnNum, gfxData) values (@index1, @gfxLine) --move data pointer forward one row, back one column set @dataPtr = @dataPtr + @rowWidth - @colWidth set @index2 = @index2 + 1 end --move data pointer back to bottom or data (top of next column) set @dataPtr = @dataPtr - @rowWidth * @imageHeight + @colWidth set @index1 = @index1 + 1 end
Save Circle Land
- Play it!
A Flash vertical-shooting game; in progress. You control the virtuous Triangle, fighting to rid Circle Land of all other evil polygons! (Press ENTER to start, then arrows to move and CTRL to shoot.)
Fancy 2600 Bankswitching Technique
The maximum memory the Atari 2600 can reference is 4K - 4096 bytes - it only has 12 address lines. This presents a pretty big limitation on the complexity of video games for the platform, and clever coders and engineers figured out ways around it almost immediately, through a process called bankswitching.
In bankswitching, you have extra hardware on the cart and, when certain memory locations are accessed, the cartridge switches out one 4K "bank" for another. The 2600 doesn't know the difference, it just keeps on blithely executing opcodes, but this gives the programmer access to much larger (relatively speaking) ROMs in which to program his game.
It does pose a difficult technical challenge for the programmer, though: when a bank is switched, the program counter doesn't change but the data and opcodes it is pointing at do change immediately. So you have to make sure that the code in different banks lines up exactly. This becomes so much of a memory management issue, though, that usually the programmer will always switch banks in the same place in memory (usually close to the end of the ROM, near 0xFFFF) and then use jump tables or something similar to keep his program flow on track.
I used a similar method for some of my games (notably Go Fish! and Reindeer Rescue, both of which were bankswitched games), but the jump-table method left me dissatisfied, for a couple of reasons:
- It requires you to maintain the same or similar jump tables in every bank, which is prone to error
- If the jump tables are large, it uses a lot of precious memory in each bank
- It requires you, still, to do a lot of memory management and keep track of routines
- Most importantly, though, it doesn't provide any easy way to return from a subroutine in another bank
So I came up with a solution, using the (normally unused) BRK vector:
In every bank, have the break vector (the word at 0xFFFE) point at this subroutine, also in every bank at the same memory location:
plp ;pull processor flags off stack (and discard) tsx ;get stack pointer into X dec $01,X ;decrement the address pushed on the stack by BRK lda ($01,X) ;read the low byte of the address directly after the ;BRK opcode that sent us here sta MiscPtr ;save it inc $01,X ;increment address pushed on the stack by BRK lda ($01,X) ;read the high byte of the address sta MiscPtr+1 ;save it lsr lsr lsr lsr lsr tax ;save the top 3 bits of the high byte of the address ;and put in X nop $1FF4,X ;this is the code that switches banks ;which byte I access in the range $1FF4 - $1FFB ;determines which bank I switch to jmp (MiscPtr) ;after the bankswitch, I land here and jump
You call this routine with the BRK command, like this:
brk .word SubroutineAddress
The trick here is that when the 6502 encounters a BRK opcode it pushes the current address + 1 and the flags to the stack and then jumps directly to the address pointed at by the BRK vector. Since I have the address I came from (on the stack), I just read the address of the subroutine (stored right) after the BRK opcode and jump there.
Now since the 2600 only has 12 address lines, I can use the top 4 bits of an address for my own purposes. In this case, I use three of them to tell me which bank to switch to. If I ORG each bank to a different 4K block and then I can move subroutines around within a bank or even from bank to bank and the code automatically can find its way there. And returning is just as easy; at the end of a subroutine just jump here:
tsx inx inx lda $00,X ;get high byte of return address lsr lsr lsr lsr lsr tax nop $1FF4,X rts
Basically the same routine; just look at the return address on the stack, grab the top 3 bits to determine which bank holds our destination, switch to that bank, and RTS to where we came from.