<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1155250278529986220</id><updated>2012-03-09T11:53:55.694-08:00</updated><title type='text'>Prince of Persia C64 - Development Blog</title><subtitle type='html'>This blog will document the development history of the C64 conversion of Prince of Persia.
I will talk you through the steps that were taken and explain the technical details of how the game works and how I ended up with a Commodore 64 version.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-8163600297062035447</id><published>2012-03-02T14:24:00.000-08:00</published><updated>2012-03-02T14:24:34.407-08:00</updated><title type='text'>Part Ten - The final push</title><content type='html'>The last few months of the project were pretty unexciting. There's a certain mode that you get in, if you can see a project coming to an end. It's basically becoming a bit of a grind, where you're trying to finish off as many of the lose ends that are still left. After all, you want to get this over with, and there's no easy way of doing that.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-K232XkOjUsY/T1FHZyVR_5I/AAAAAAAAAKU/B8WOFk2TDOs/s1600/end.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="200" width="320" src="http://4.bp.blogspot.com/-K232XkOjUsY/T1FHZyVR_5I/AAAAAAAAAKU/B8WOFk2TDOs/s320/end.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Having been through this a few times before, I knew that the best way for me is to make a TODO list now, of every thing that still needs to be done, which had about 100 items in the end. Ticking things off that list helps in seeing the light at the end of the tunnel...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    ...&lt;br /&gt;    [x] Add title screen graphics and logic&lt;br /&gt;    [x] Add cheat keys&lt;br /&gt;    [x] Fix Jaffar positioning in level 13&lt;br /&gt;    [x] Fix shadow not running fully out of screen&lt;br /&gt;    [x] Make strength indicator blink if only one left&lt;br /&gt;    [x] Fix little drawing glitch when C section is overlapping top block area&lt;br /&gt;    ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One of the remaining things was the frame of the game: The title screen along with the text cards inspired by the silent movie era. I knew that I wouldn't have to reverse-engineer that code, as it would be much easier to achieve the same thing and faster using custom code. This was one of the easiest parts and very satisfying work.&lt;br /&gt;&lt;br /&gt;I started out with STE's initial work on the background for the title screens. I did take the liberty to change it a bit to make it look more like the graphics in the PC version (mostly the border of the image).&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-Gk_Wa4Kk_v0/T01PXCDgC5I/AAAAAAAAAJQ/ApKdviDdR9g/s1600/title_01_background.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-Gk_Wa4Kk_v0/T01PXCDgC5I/AAAAAAAAAJQ/ApKdviDdR9g/s1600/title_01_background.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Original title background from DOS version (by Avril Harrison)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-yRCy6upAdgk/T01RaefYsoI/AAAAAAAAAJY/aSkT3HrzN1g/s1600/pop_title_bg2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-yRCy6upAdgk/T01RaefYsoI/AAAAAAAAAJY/aSkT3HrzN1g/s1600/pop_title_bg2.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;C64 multi-color background without ornaments (by Steve Day)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div&gt;For the little symbols in the corners, I decided to go with hires sprites, because the 160x200 multicolor mode used by the picture made them look really bad, and I wanted to be pixel precise where possible. STE had the idea to add his own signature in the lower left corner, instead of Avril Harrison's "ah".&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-qUYeAYkqC2Q/T01R0ybzapI/AAAAAAAAAJg/QX1kh0r9_DQ/s1600/pop_title_bg.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-qUYeAYkqC2Q/T01R0ybzapI/AAAAAAAAAJg/QX1kh0r9_DQ/s1600/pop_title_bg.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;With hi-res sprite overlays in the corners&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div&gt;At this point I was wondering if I should put my own name and copyright notice into the title screen. But doing that felt wrong somehow. So I decided to go with what Brøderbund would've made back in the days: The same logo and "a game by Jordan Mechner", which seemed appropriate.&lt;br /&gt;I added versions of the background image with black outlines for white&amp;nbsp;hi-res sprite overlays. Although the outlines are not pixel precise (since they're in 160x200 resolution), they're good enough when covered with the sprites to make the final image look a lot like the PC screens.&lt;/div&gt;&lt;br&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-6WIoacgBcLY/T01U8GpdU2I/AAAAAAAAAJw/nFOcACeNTwc/s1600/outlines.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-6WIoacgBcLY/T01U8GpdU2I/AAAAAAAAAJw/nFOcACeNTwc/s1600/outlines.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Black outlines in background image for Brøderbund logo&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-5FPMiX03pFo/T01VNhugLxI/AAAAAAAAAJ4/tGbN9owlllA/s1600/broderbund_overlay.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-5FPMiX03pFo/T01VNhugLxI/AAAAAAAAAJ4/tGbN9owlllA/s1600/broderbund_overlay.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Sprite overlay for Brøderbund logo&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/-XX-JuhXdSgY/T1B9gA0DdpI/AAAAAAAAAKI/yakN139CFW0/s1600/logo_composite.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-XX-JuhXdSgY/T1B9gA0DdpI/AAAAAAAAAKI/yakN139CFW0/s1600/logo_composite.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;The composited result&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;div&gt;For the title cards containing the text which explains the game's story, I had a similar plan. I also wanted to make sure to get the full 320x200 graphics ported over, since any 160x200 experiment I made was looking awful.&lt;/div&gt;&lt;div&gt;The trick was simple, I just used raster interrupts to switch from lo-res to hi-res bitmap mode in the area where the text needs to be displayed. Since that would also affect the border on the left and right edge of the screen I simply put a multiplexed column of multi-color sprites there. It doesn't get much easier than that.&lt;/div&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-kkSMqySUytk/T01XTzdVLrI/AAAAAAAAAKA/g2SXP_40K0w/s1600/text_bg.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-kkSMqySUytk/T01XTzdVLrI/AAAAAAAAAKA/g2SXP_40K0w/s1600/text_bg.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;One of the text screens, without the sprites used for the border&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;div&gt;I added the necessary logic and drawing code to flip through the different screens. That wasn't hard because I used the two screen buffers which were already set up for the game, but it needed some care to hide drawing glitches that were be visible, by making sure that the color RAM updates happen at the right times during the screen refresh.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;To complete the flow of the title screen, I had to add the intro cutscene now. It's certainly the most complicated of all the cutscenes in the game, but fortunately I learned enough about that already, so it didn't really pose a challenge anymore. Here's the code for that cutscene:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;cutsceneBeforeLevel1:&lt;br /&gt;            jsr triggerVizierStanding&lt;br /&gt;            jsr saveKid&lt;br /&gt;            jsr triggerPrincessStanding&lt;br /&gt;            jsr saveShad&lt;br /&gt;            lda #$02&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; princess waiting (just music)&lt;br /&gt;            lda #$07&lt;br /&gt;            ldx #$08&lt;br /&gt;            jsr runCutsceneLoopWithMusic&lt;br /&gt;            lda #$05&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; princess turns around&lt;br /&gt;            lda #$62&lt;br /&gt;            jsr triggerShadSequence&lt;br /&gt;            lda #$09&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;&lt;br /&gt;            ; door opens (music used as sound effect)&lt;br /&gt;            lda #$08&lt;br /&gt;            ldx #$00&lt;br /&gt;            jsr runCutsceneLoopWithMusic&lt;br /&gt;            lda #$07&lt;br /&gt;            sta CutsceneDelay&lt;br /&gt;            lda #$05&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; jaffar enters&lt;br /&gt;            lda #$60&lt;br /&gt;            jsr triggerKidSequence&lt;br /&gt;            lda #$06&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            lda #$61&lt;br /&gt;            jsr triggerKidSequence&lt;br /&gt;            lda #$04&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; jaffar stands (just music)&lt;br /&gt;            lda #$09&lt;br /&gt;            ldx #$0c&lt;br /&gt;            jsr runCutsceneLoopWithMusic&lt;br /&gt;            lda #$04&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; jaffar approaches&lt;br /&gt;            lda #$60&lt;br /&gt;            jsr triggerKidSequence&lt;br /&gt;            lda #$1e&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            lda #$61&lt;br /&gt;            jsr triggerKidSequence&lt;br /&gt;            lda #$04&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; jaffar stands in front of princess (tension building music)&lt;br /&gt;            lda #$0a&lt;br /&gt;            ldx #$19&lt;br /&gt;            jsr runCutsceneLoopWithMusic&lt;br /&gt;            &lt;br /&gt;            ; jaffar reaching for princess&lt;br /&gt;            lda #$66&lt;br /&gt;            jsr triggerKidSequence&lt;br /&gt;            lda #$01&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; princess retreats&lt;br /&gt;            lda #$63&lt;br /&gt;            jsr triggerShadSequence&lt;br /&gt;            lda #$0d&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; jaffar spawns hour glass&lt;br /&gt;            ldx #$00&lt;br /&gt;            jsr initHourglass&lt;br /&gt;            lda #$05&lt;br /&gt;            sta ScreenFlashLength&lt;br /&gt;            lda #$ff&lt;br /&gt;            sta ScreenFlashFillByte&lt;br /&gt;            lda #$0c&lt;br /&gt;            sta CutsceneDelay&lt;br /&gt;            lda #$05&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            lda #$00&lt;br /&gt;            sta CutsceneSandCounter&lt;br /&gt;            lda #$0b&lt;br /&gt;            ldx #$08&lt;br /&gt;            jsr runCutsceneLoopWithMusic&lt;br /&gt;            lda #$07&lt;br /&gt;            sta CutsceneDelay&lt;br /&gt;            &lt;br /&gt;            ; jaffar turns around and leaves (sand starts falling)&lt;br /&gt;            lda #$64&lt;br /&gt;            jsr triggerKidSequence&lt;br /&gt;            lda #$11&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            ldx #$01&lt;br /&gt;            jsr initHourglass&lt;br /&gt;            lda #$0c&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; princess is sad&lt;br /&gt;            lda #$71&lt;br /&gt;            jsr triggerShadSequence&lt;br /&gt;            lda #$1c&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            &lt;br /&gt;            ; jaffar has left the screen&lt;br /&gt;            lda #$0c&lt;br /&gt;            sta CutsceneDelay&lt;br /&gt;            lda #$0c&lt;br /&gt;            ldx #$14&lt;br /&gt;            jmp runCutsceneLoopWithMusic&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Like in the other cutscenes, the princess uses the Shad slot, and in this case Jaffar is using the Kid slot. Since the Apple II is challenged when it comes to sound and music, you can't have animation and music at the same time. So they alternate each other. A bit of animation, then a bit of music and so on.&lt;br /&gt;The timing is either determined by the number of frames passed to runCutsceneLoop (in case of an animated part) or the length of the music (in case of runCutsceneLoopWithMusic). If music is disabled, then runCutsceneLoopWithMusic just calls runCutsceneLoop and uses the X register contents as the number of frames.&lt;br /&gt;For now I had no music system in place, so all I got was that alternative timing:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/pgbUeFj1Jdw/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/pgbUeFj1Jdw?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/pgbUeFj1Jdw?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;After I got this intro cutscene working initially, I spent some time to tweak the character positioning to match the background graphics as good as possible and to make sure that the distance between princess, vizier and the hourglass is proper, since the general layout of the screen differs a bit from the Apple II version.&lt;/div&gt;&lt;br /&gt;Oh, but what about the music? Turns out that is a longer story...&lt;br /&gt;I asked a few different musicians while working on the game, and even though they showed some interest, it quickly became apparent to me that the idea of converting music is not as appealing to a composer as writing something original is. &lt;br /&gt;So after trying to get someone else to do it for some time, I hit a point where getting some music into the game became a high priority, and that's when I decided to give it a go myself.&lt;br /&gt;Now I have to add that even though I know lots of things about the SID, ripped many tunes during my adolescent years and generally am a huge fan of C64 music, one thing I've never done myself is write a SID music routine. Mostly because I'm not a composer, and writing a player also typically means that you have to write an editor for it, which feels like a lot of tedious work.&lt;br /&gt;&lt;br /&gt;So having decided to make my own music conversion (mostly as a test, because back then I was still hoping that a real musician would come forward with some work) I first did some reverse-engineering into the music player of the Apple II version, out of interest. Well, let's just say that that was funny and scary at the same time and I quickly stopped doing that. :)&lt;br /&gt;&lt;br /&gt;I then took a closer look at the MIDI files that I had extracted from the data of the PC version. I wrote a little Python script to parse the MIDI files so I could examine the tracks and the note data they contain. One thing that was important to me was to see how many tracks the music had and if it was feasible to play it using just the three voices of the SID.&lt;br /&gt;Turns out that I was really lucky there, because even though the MIDI files had more than 3 tracks, those were generally not essential to the music, e.g. most of the additional tracks were just there to make a certain instrument fatter by playing the same sequence of notes one octave down or up. Those tracks were labelled "Bass" and "Bass dbl-v" or "melody" and "melody dbl". Removing the dbl tracks from the MIDI files made each file contain just 3 tracks, and the music was still complete and easily recognizable, i.e. it didn't miss large chunks or whole instruments.&lt;br /&gt;It seemed like someone had already planned it like this.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object data="http://twinbirds.com/prince_of_persia/xspf_player_slim.swf?&amp;amp;song_url=http://twinbirds.com/prince_of_persia/tune_01_02_03.mp3&amp;amp;song_title=Full%20title%20theme" height="15" type="application/x-shockwave-flash" width="320"&gt;&lt;param name="movie" value="http://twinbirds.com/prince_of_persia/xspf_player_slim.swf?&amp;song_url=http://twinbirds.com/prince_of_persia/tune_01_02_03.mp3&amp;song_title=Full%20title%20theme" /&gt;&lt;a href="http://twinbirds.com/prince_of_persia/tune_01_02_03.mp3""&gt;Full title theme&lt;/a&gt;&lt;/object&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object data="http://twinbirds.com/prince_of_persia/xspf_player_slim.swf?&amp;amp;song_url=http://twinbirds.com/prince_of_persia/tune_01_02_03_reduced.mp3&amp;amp;song_title=Reduced%20title%20theme%20to%20only%203%20tracks" height="15" type="application/x-shockwave-flash" width="320"&gt;&lt;param name="movie" value="http://twinbirds.com/prince_of_persia/xspf_player_slim.swf?&amp;song_url=http://twinbirds.com/prince_of_persia/tune_01_02_03_reduced.mp3&amp;song_title=Full%20title%20theme%20to%20only%203%20tracks" /&gt;&lt;a href="http://twinbirds.com/prince_of_persia/tune_01_02_03_reduced.mp3"&gt;Reduced title theme to only 3 tracks&lt;/a&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;So the plan was clear. I needed to get the data out of the MIDI file and into shape for a C64 music player. And I had to write one. The first problem that came up was the issue of timing. MIDI files can have various different tempi, and they're usually specified in ticks per quarter note, and ticks can be very precise. Also they typically don't nicely line up with the 50Hz or 60Hz ticks of SID players which are driven by the screen refresh.&lt;br /&gt;But since the Apple II is so restricted in the music department, it meant that I didn't have to clock my music player that way. Whenever music is supposed to play in the Apple II version, the game pauses and waits until the music is done. In the PC version there's some music overlapping the actual game play, but it's not really required. The game still works if game and music are mutually exclusive.&lt;br /&gt;That meant that I could use a timer interrupt to drive the music, and the music player could change the timer count rate to have true variable tempo with precise timing, something which is usually never done with SID music.&lt;br /&gt;&lt;br /&gt;So I started by adding code to the Python script to save out the note data in a binary format suitable for C64 use. I then implemented a simple playback engine that reads this data and plays it on the SID. I added some simple effects for the instruments, such as vibrato and pulse/filter sweeps. Nothing too fancy, but enough to make it sound better than a music program in BASIC. Luckily the music didn't use drums.&lt;br /&gt;&lt;br /&gt;Since at this point I had no RAM left, I fit the whole player and all the music data into a single 8KB ROM bank, so it can be banked in and executed easily. The data is quite large, since there are no patterns to be reused, it is basically a stream of notes and instrument changes, with timing information. &lt;br /&gt;You can listen to the result here:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object data="http://twinbirds.com/prince_of_persia/xspf_player_slim.swf?&amp;amp;song_url=http://twinbirds.com/prince_of_persia/tune_01_02_03_sid.mp3&amp;amp;song_title=SID%20version%20of%20title%20theme" height="15" type="application/x-shockwave-flash" width="320"&gt;&lt;param name="movie" value="http://twinbirds.com/prince_of_persia/xspf_player_slim.swf?&amp;song_url=http://twinbirds.com/prince_of_persia/tune_01_02_03_sid.mp3&amp;song_title=SID%20version%20of%20title%20theme" /&gt;&lt;a href="http://twinbirds.com/prince_of_persia/tune_01_02_03_sid.mp3"&gt;SID version of title theme&lt;/a&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;Putting it all together, I still had to add code to sync the title screen updates with the music timing. I hope the result is respectable, considering it's my first SID music player... :)&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/C8DDd7gEnkc/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/C8DDd7gEnkc?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/C8DDd7gEnkc?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I guess this kinda concludes this story. I wouldn't want to bore you with details about the three months of final testing and bug fixing that I did. Or the endless amount of pixel fine-tuning on some of the graphics (Thanks, Mikael!). Or how Conrad jumped in at the very end to provide some awesome sound effects in practically no time, using custom code.&lt;br /&gt;&lt;br /&gt;Once again, you can download the &lt;a href="http://www.twinbirds.com/prince_of_persia/prince_of_persia_apple2_disasm.zip"&gt;Apple II memory dump disassembly&lt;/a&gt; that I built up during this project, which might be helpful to port Prince of Persia to other platforms (not necessarily just 6502-based ones). I hope you had a good time reading this, and maybe I'll have some more interesting stuff to talk about in the future. Until then, take care, and keep the C64 spirit alive.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-8163600297062035447?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/8163600297062035447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2012/03/part-ten-final-push.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/8163600297062035447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/8163600297062035447'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2012/03/part-ten-final-push.html' title='Part Ten - The final push'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-K232XkOjUsY/T1FHZyVR_5I/AAAAAAAAAKU/B8WOFk2TDOs/s72-c/end.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-5924916353584698214</id><published>2012-01-14T14:54:00.000-08:00</published><updated>2012-01-14T14:54:22.103-08:00</updated><title type='text'>Part Nine - Optimizations for the Princess</title><content type='html'>Let me tell you a thing about airplane coding. It's where I get things done. As soon as the plane has taken off and it's fine to use electronic devices again, I put on my headphones and start getting productive. Trying to cram as much work into a few hours allows me to work in a very focused manner.&lt;br /&gt;That was also true on my flight to Barcelona. The stuff that I struggled with before was suddenly much clearer. I came up with a really good optimization for the redrawing of background graphics, which allowed the game to run at a much smoother frame rate.&lt;br /&gt;Just before I went on the flight I added profiling code for the bitmap drawing routines. I needed to measure how much time is spent pushing pixels. Traditionally on the C64 this is achieved by changing the screen colors to visualize the raster time used by a function. &lt;br /&gt;&lt;br /&gt;The initial measurement was quite sobering. It looked like this:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Ew7KTQogin8/Tw3xq1j25FI/AAAAAAAAAIw/Zn47u6pLzQk/s1600/before_opt.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-Ew7KTQogin8/Tw3xq1j25FI/AAAAAAAAAIw/Zn47u6pLzQk/s1600/before_opt.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Drawing the flames, before optimization&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;The green color means it's drawing an opaque bitmap (i.e. black pixels overwrite the destination). Red means that the width of the bitmap is 2 bytes (16 pixels). These bars therefore represent the redrawing of the torches. You can see that it takes about 37 raster lines which is about 2.3ms.&lt;br /&gt;After optimization it looked more like this:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Zs6O2ttjdoI/Tw4HITm51eI/AAAAAAAAAI4/gXPx1QByfcI/s1600/after_opt.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-Zs6O2ttjdoI/Tw4HITm51eI/AAAAAAAAAI4/gXPx1QByfcI/s1600/after_opt.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Drawing the flames, after optimization&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;The time spent in redrawing the flame bitmaps is now only about 0.9ms. Naturally the same speedup also applied to all other redrawing of transitional objects, so jiggling floors, raising/lowering gates and spikes shooting out of the floor were all much smoother now.&lt;br /&gt;With this thing off my mind, I was able to have a relaxed time in Barcelona. On my flight back I did add the little damage indicators that appear when a character gets hit.&lt;br /&gt;&lt;br /&gt;Now I did try to optimize this code before, but didn't really make this much progress. The main idea now was to specialize the code for every possible width (1, 2, 3, and 4 bytes) and iterate for each row. This meant that the code required a lot more memory than before, which was problematic because it needed to be in RAM. During drawing the ROM has to be turned off, as it covers the same memory area as the bitmap buffers. For non-opaque drawing, I have to read from the bitmap and then OR the new pixels on top of that.&lt;br /&gt;&lt;br /&gt;Opaque drawing is easier, it basically boils down to this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;draw2ColumnsOpaque: &lt;br /&gt;{&lt;br /&gt;        ldx #$00&lt;br /&gt;        {&lt;br /&gt;            lda BgImageBuffer+2,x   ; read byte from image&lt;br /&gt;            ldy #$00&lt;br /&gt;            sta (BitmapPtr),y       ; and store in bitmap&lt;br /&gt;            inx&lt;br /&gt;&lt;br /&gt;            lda BgImageBuffer+2,x   ; read byte from image&lt;br /&gt;            ldy #$08&lt;br /&gt;            sta (BitmapPtr),y       ; and store in bitmap&lt;br /&gt;            inx&lt;br /&gt;            &lt;br /&gt;            lda BitmapPtr&lt;br /&gt;            and #$07&lt;br /&gt;            beq oneRowUp&lt;br /&gt;            &lt;br /&gt;            dec BitmapPtr&lt;br /&gt;            dec BlockImageVisibleHeight&lt;br /&gt;            bne _cont&lt;br /&gt;            jmp endDraw&lt;br /&gt;&lt;br /&gt;oneRowUp:               &lt;br /&gt;            lda BitmapPtr               ; move one char row up&lt;br /&gt;            sec&lt;br /&gt;            sbc #$39&lt;br /&gt;            sta BitmapPtr&lt;br /&gt;            &lt;br /&gt;            lda BitmapPtr+1&lt;br /&gt;            sbc #$01&lt;br /&gt;            sta BitmapPtr+1&lt;br /&gt;        &lt;br /&gt;            dec BlockImageVisibleHeight&lt;br /&gt;            bne _cont&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        jmp endDraw&lt;br /&gt;;----------------------------------&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For transparent images (i.e. black pixels are not written) there's a lot more work necessary:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;draw3ColumnsNormal:&lt;br /&gt;{&lt;br /&gt;        ldx #$00&lt;br /&gt;        {&lt;br /&gt;            ldy #$00&lt;br /&gt;            lda (BitmapPtr),y       ; read from bitmap &lt;br /&gt;            ldy BgImageBuffer+2,x   ; read byte from image&lt;br /&gt;            and MaskTable,y         ; get masking outline of image byte and clear covered bits in bitmap&lt;br /&gt;            ora BgImageBuffer+2,x   ; now or the image bytes on top of the background&lt;br /&gt;            ldy #$00&lt;br /&gt;            sta (BitmapPtr),y       ; and store in bitmap&lt;br /&gt;            inx&lt;br /&gt;&lt;br /&gt;            ldy #$08&lt;br /&gt;            lda (BitmapPtr),y       ; read from bitmap &lt;br /&gt;            ldy BgImageBuffer+2,x   ; read byte from image&lt;br /&gt;            and MaskTable,y         ; get masking outline of image byte and clear covered bits in bitmap&lt;br /&gt;            ora BgImageBuffer+2,x   ; now or the image bytes on top of the background&lt;br /&gt;            ldy #$08&lt;br /&gt;            sta (BitmapPtr),y       ; and store in bitmap&lt;br /&gt;            inx&lt;br /&gt;&lt;br /&gt;            ldy #$10&lt;br /&gt;            lda (BitmapPtr),y       ; read from bitmap &lt;br /&gt;            ldy BgImageBuffer+2,x   ; read byte from image&lt;br /&gt;            and MaskTable,y         ; get masking outline of image byte and clear covered bits in bitmap&lt;br /&gt;            ora BgImageBuffer+2,x   ; now or the image bytes on top of the background&lt;br /&gt;            ldy #$10&lt;br /&gt;            sta (BitmapPtr),y       ; and store in bitmap&lt;br /&gt;            inx&lt;br /&gt;&lt;br /&gt;            lda BitmapPtr&lt;br /&gt;            and #$07&lt;br /&gt;            beq oneRowUp                &lt;br /&gt;&lt;br /&gt;            dec BitmapPtr&lt;br /&gt;            dec BlockImageVisibleHeight&lt;br /&gt;            bne _cont&lt;br /&gt;            jmp endDraw&lt;br /&gt;&lt;br /&gt;oneRowUp:               &lt;br /&gt;            lda BitmapPtr               ; move one char row up&lt;br /&gt;            sec&lt;br /&gt;            sbc #$39&lt;br /&gt;            sta BitmapPtr&lt;br /&gt;            &lt;br /&gt;            lda BitmapPtr+1&lt;br /&gt;            sbc #$01&lt;br /&gt;            sta BitmapPtr+1&lt;br /&gt;        &lt;br /&gt;            dec BlockImageVisibleHeight&lt;br /&gt;            bne _cont&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        jmp endDraw&lt;br /&gt;;----------------------------------&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;At this point (in early June 2011) I was hitting the most important milestone: Being able to play through the whole game. What's generally known as an "alpha" build. Some things didn't quite work 100% (end fight with Jaffar was messed up), some things were missing (no falling floors), and the graphics for the palace levels were mostly just dungeon tiles with different colors.&lt;br /&gt;&lt;br /&gt;That meant that I had to tackle the next big unknown, the cutscene animation system. I previously only found bits and pieces of this code and was unsure how much there was. I was hoping that it would fit into my current structure. I decided to first concentrate on the in-game cutscenes (between certain levels) and the final cutscene of the game first, as those would not require the whole title screen logic, which I didn't have yet. I knew that those cutscene must be triggered from the game code somewhere.&lt;br /&gt;Luckily I had already received graphics for the princess room and all the animations of the princess and vizier from STE. But I first had to find out how to put them to use. So I was back into doing serious reverse engineering.&lt;br /&gt;&lt;br /&gt;At the end of the mainLoop there's this end condition:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;            lda NextLevel&lt;br /&gt;            cmp CurrentLevel&lt;br /&gt;            beq mainLoop         ; nope, level hasn't changed, keep playing&lt;br /&gt;startNextLevel:&lt;br /&gt;            jsr l27e5&lt;br /&gt;            jmp activateNextLevelOrCutscene&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The code at l27e5 is something I haven't spent much time looking at. My hunch is that it's showing the message prompting the user to insert the correct disk for the upcoming level. Not something I was interested in.&lt;br /&gt;The jump to activateNextLevelOrCutscene is more interesting, not just because it's completely unnecessary, since activateNextLevelOrCutscene begins immediately after this instruction.&lt;br /&gt;It calls checkForCutscene which looks at the NextLevel variable and branches to different pieces of code for levels 2, 4, 6, 8, 9 and 12. Turns out these are the levels that begin with a cutscene, showing the princess waiting for the kid in some form. For any other level the code branches immediately to initLevel.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;            lda NextLevel&lt;br /&gt;            sta CurrentLevel   ; the next level is now the current level&lt;br /&gt;            cmp #$02&lt;br /&gt;            beq l2225          ; branch to cutscene for level 2&lt;br /&gt;l220e:&lt;br /&gt;            cmp #$04&lt;br /&gt;            beq l223a          ; branch to cutscene for level 4&lt;br /&gt;l2212:&lt;br /&gt;            cmp #$06&lt;br /&gt;            beq l223e          ; branch to cutscene for level 6&lt;br /&gt;l2216:&lt;br /&gt;            cmp #$08&lt;br /&gt;            beq l224a          ; branch to cutscene for level 8&lt;br /&gt;l221a:&lt;br /&gt;            cmp #$09&lt;br /&gt;            beq l2242          ; branch to cutscene for level 9&lt;br /&gt;l221e:&lt;br /&gt;            cmp #$0c&lt;br /&gt;            beq l2246          ; branch to cutscene for level 12&lt;br /&gt;endCutscene:&lt;br /&gt;            jmp initLevelImpl  ; next level has no cutscene, so just start it immediately&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's take the cutscene before level 2 as an example. This is just the generic "princess waits while standing" scene:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-GoMWq-aqwF4/TxFMGwfsixI/AAAAAAAAAJA/bcQ_z2IJzaE/s1600/cutscene2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-GoMWq-aqwF4/TxFMGwfsixI/AAAAAAAAAJA/bcQ_z2IJzaE/s1600/cutscene2.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Just standing here, waiting for my prince to come...&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;l2225:&lt;br /&gt;            lda #$01                     ; cutscene id&lt;br /&gt;l2227:&lt;br /&gt;            pha&lt;br /&gt;retry:&lt;br /&gt;            jsr loadCutsceneBackground   ; loads the bitmap for the princess room&lt;br /&gt;            jsr l4c4b                    ; Apple II specific file error handling?&lt;br /&gt;            jsr l4c4e                    ; Apple II specific file error handling?&lt;br /&gt;            bne retry&lt;br /&gt;l2233:&lt;br /&gt;            pla&lt;br /&gt;            jsr triggerCutscene          ; execute the cutscene&lt;br /&gt;            jmp endCutscene              ; jumps to initLevel, continues game after cutscene&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So far so good. I identified triggerCutscene to be the main entry point for all cutscenes, which gets the cutscene id passed in through the accumulator.&lt;br /&gt;It looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;triggerCutsceneImpl:&lt;br /&gt;            pha&lt;br /&gt;            jsr initCutscene&lt;br /&gt;            pla&lt;br /&gt;            tax&lt;br /&gt;            lda CutsceneJumpTableLo,x&lt;br /&gt;            sta le214&lt;br /&gt;            lda CutsceneJumpTableHi,x&lt;br /&gt;            sta le215&lt;br /&gt;    le214 = * + 1&lt;br /&gt;    le215 = * + 2&lt;br /&gt;            jsr $ffff&lt;br /&gt;            lda #$01&lt;br /&gt;            sta CutsceneDelay&lt;br /&gt;            rts&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Oh, this is as easy as it gets. A simple jump table for each cutscene. The entry for level 2 is also used for level 6 (since it's the same cutscene) and the function it points to is very short:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;cutsceneBeforeLevel2And6:&lt;br /&gt;            jsr getTimeLeftForCutscene ; check how much time is left in the game&lt;br /&gt;            jsr initCutsceneTimers  ; initializes hour glass state and sand animation timer&lt;br /&gt;            jsr triggerPrincessStandingFacingRight&lt;br /&gt;            jsr saveShad&lt;br /&gt;            lda #$02                ; run the cutscene loop for two frames&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            ldx #$32                ; run for this many frames if music is disabled                &lt;br /&gt;            lda #$0d                ; id of the background music, cutscene runs until music ends&lt;br /&gt;            jmp cutsceneLoopWithOptionalCharacterAnim&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The core of it is triggerPrincessStandingFacingRight:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;triggerPrincessStandingFacingRight:&lt;br /&gt;            jsr triggerPrincessStanding    ; spawn princess (looks left by default)&lt;br /&gt;            lda #$00                       ; make her look right instead&lt;br /&gt;            sta CharFace&lt;br /&gt;            rts&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;br /&gt;;----------------------------------&lt;br /&gt;triggerPrincessStanding:&lt;br /&gt;            lda #$05                       ; princess uses CharID 5 (see below)&lt;br /&gt;            sta CharID&lt;br /&gt;            lda #$78                       ; X position&lt;br /&gt;            sta CharX&lt;br /&gt;            lda #$97                       ; Y position&lt;br /&gt;            sta CharY&lt;br /&gt;            lda #$ff                       ; looking left&lt;br /&gt;            sta CharFace&lt;br /&gt;            lda #$5e                       ; standing sequence&lt;br /&gt;            jsr setSequence&lt;br /&gt;            jmp animChar                   ; execute first frame of sequence&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I'd seen this last bit of code before, when I was looking at all the places where setSequence is used. Back then I wasn't able to identify all of the sequences, because some of them just seemed broken. Turns out that those were cutscene animations, and they were in the normal sequence table already. So there's no animation sequence data loaded for the cutscene. They obviously looked weird when played with the Kid frame def list.&lt;br /&gt;&lt;br /&gt;So basically the cutscene system is just using the normal game engine to draw the characters. They have special CharIDs as described in the PoP source code documentation. &lt;br /&gt;&lt;br /&gt;5 = princess (in princess scenes)&lt;br /&gt;6 = vizier (in princess scenes)&lt;br /&gt;&lt;br /&gt;Since cutsceneBeforeLevel2And6 calls saveShad, the princess is actually drawn as a guard.&lt;br /&gt;Just like for guards, reading from the frame def list is overridden for these characters by overrideFrameDefListForCharacter, so it's going through indexIntoCutsceneFrameDefList rather than the normal code. &lt;br /&gt;&lt;br /&gt;After setting up the CharData for the princess and saving it to the Shad slot, the game runs runCutsceneLoop which is a little mini game loop just for the cutscenes. It handles keyboard and joystick (to be able to skip the scene), updates the character animations (much like the game itself does) and updates the screen:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;updateCutsceneScreen:&lt;br /&gt;            jsr drawCutsceneFrame&lt;br /&gt;            jsr waitForVBlank&lt;br /&gt;            jmp flipFrontAndBackBuffer&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In drawCutsceneFrame we find code to draw the hour glass, the flames of the torches, the stars outside the window and the sand of the hour glass. This is all very specialized code, but it uses the existing drawing functions used by the game to blit images onto the screen.&lt;br /&gt;&lt;br /&gt;It was relatively easy now to get this system up and running. First I replaced loadCutsceneBackground with my own code that copies a multicolor bitmap into both screen buffers and draws a rectangle for the right pillar into the mask bitmap.&lt;br /&gt;&lt;br /&gt;The hourglass drawing I replaced with a very simple copy loop that stamps one of the hourglass images onto the normal princess room bitmap (and screen and color RAM). There are 9 different images used, depending on the current time left in the game. At the beginning the hourglass is still full and in the end it's empty. I also made two variants which are alternated between to get the sand animation going. The Apple II original had special sand animation images which were drawn on top of the hourglass, but I decided that this wasn't really necessary, so my code just redraws the whole hourglass every time. No problem.&lt;br /&gt;&lt;br /&gt;The blinking stars outside the windows were also more easily solved on the C64. I just placed them nicely in the bitmap and then added some code to play around with the color RAM entries. Easy enough.&lt;br /&gt;&lt;br /&gt;The flames are the same as the ones used in the level background graphics, and a lot of the drawing code is shared with the in-game code.&lt;br /&gt;&lt;br /&gt;And after adding some special code to switch the lower half of the character sprites to use a different color I finally had the princess on the screen in her room, as expected. This made me really happy.&lt;br /&gt;&lt;br /&gt;I now started to quickly decipher the other cutscene code, which was all very similar. I decided to do the final cutscene of the game next, because that one seemed the most complicated to me. From converting the animation images I knew that at some point it switches from using two characters (for princess and kid) to a single one for both, as they embrace.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;cutsceneFinal:&lt;br /&gt;            lda #$08&lt;br /&gt;            sta CutsceneDelay&lt;br /&gt;            lda #$01&lt;br /&gt;            sta SoundEnabled&lt;br /&gt;            sta MusicEnabled&lt;br /&gt;            jsr triggerPrincessStandingFacingLeftBeforeTurn&lt;br /&gt;            jsr saveShad&lt;br /&gt;            lda #$08&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            jsr triggerKidArriving  ; plays kid running sequence (facing left)&lt;br /&gt;            jsr saveKid&lt;br /&gt;            lda #$08&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            lda #$6c&lt;br /&gt;            jsr triggerShadSequence ; princess turning around and embracing kid&lt;br /&gt;            lda #$05&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            lda #$0d&lt;br /&gt;            jsr triggerKidSequence  ; kid stops from run&lt;br /&gt;            lda #$02&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            lda #$00&lt;br /&gt;            sta KidFrame            ; disable kid, the embrace has both characters in one image&lt;br /&gt;            lda #$09&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            lda #$0f               ; the music to play&lt;br /&gt;            jsr le449              ; just animates cutscene background graphics&lt;br /&gt;            jsr triggerMouseJoiningPrincessAndKid ; mouse comes in and joins the couple&lt;br /&gt;            jsr saveKid&lt;br /&gt;            lda #$0c&lt;br /&gt;            jsr runCutsceneLoop&lt;br /&gt;            lda #$65               ; stop mouse - I changed this to #$72 to make&lt;br /&gt;            jsr triggerKidSequence ; the mouse rise like in the PC version&lt;br /&gt;            lda #$1e&lt;br /&gt;            jmp runCutsceneLoop&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As soon as I had this cutscene somewhat working (I tweaked the positioning later to make it look nicer) I captured a video and uploaded it on June 13th 2011:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object width="384" height="290" class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/NlD5KL24WsQ/0.jpg"&gt;&lt;param name="movie" value="http://www.youtube.com/v/NlD5KL24WsQ?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/NlD5KL24WsQ?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;I was quite satisfied after having understood how the cutscene system works. It felt like being on the home stretch. But there was still some things left to do. The intro cutscene was within my grasp now, but I also needed to come up with all the title screen code. Oh, and what about the music? We'll find out about that in the next part.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-5924916353584698214?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/5924916353584698214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2012/01/part-nine-optimizations-for-princess.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/5924916353584698214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/5924916353584698214'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2012/01/part-nine-optimizations-for-princess.html' title='Part Nine - Optimizations for the Princess'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-Ew7KTQogin8/Tw3xq1j25FI/AAAAAAAAAIw/Zn47u6pLzQk/s72-c/before_opt.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-4010416027786904742</id><published>2011-12-07T14:43:00.000-08:00</published><updated>2011-12-08T06:51:40.633-08:00</updated><title type='text'>Part Eight - Everything comes to him who waits</title><content type='html'>After having put the Prince of Persia project on hiatus, I went on to do other things. One of them was a &lt;a href="http://sidmusic.org/gamebase64browser/"&gt;GameBase64 browser for Mac OS X&lt;/a&gt;&amp;nbsp;(for my own personal use)&amp;nbsp;and the other was a conversion of the Flash/iOS game &lt;a href="http://noname.c64.org/csdb/release/index.php?id=103017"&gt;Canabalt for the C64&lt;/a&gt;&amp;nbsp;(not to be confused with &lt;a href="http://noname.c64.org/csdb/release/?id=103143"&gt;C64anabalt&lt;/a&gt;, the other Canabalt version for the C64 by Paul Koller).&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://noname.c64.org/csdb/gfx/releases/103000/103017.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://noname.c64.org/csdb/gfx/releases/103000/103017.gif" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;A port of Canabalt, the popular one-button jump'n'run, was an interesting diversion&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;But Prince of Persia was still at the back of my mind, mostly because it's such a great project and I got so far already. I still intended to finish it some day, but I didn't have the energy and most of all, I couldn't see the light at the end of the tunnel. I had no plan how to continue.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;I wanted to release Canabalt in March of 2011, but as it turned out, I had to wait a long time for somebody to do the music for the game. In April I still hadn't received it, so I kinda became bored with waiting and my thoughts wandered off.&lt;br /&gt;&lt;br /&gt;Before long I found myself thinking about Prince of Persia again. I had the idea to look into C64 cartridge technology to decide if that would be a feasible route to go. I only had a very minimal idea of how some of the advanced cartridges with bank-switching logic (the ones used by Ocean and System 3) worked in detail. I started investigating and in my head I began to rearrange the memory layout of my existing code base.&lt;br /&gt;&lt;br /&gt;Which things can I move into the cartridge ROM? Which things have to stay in RAM? Where's the best place to put graphics data? How much work would it be to rewrite my existing REU-based code to get this up and running?&lt;br /&gt;I was quite aware that those cartridges offered a unique possibility. Not only would I be able to move all the big animation data into ROM, but I could also have code there and run it from ROM. This meant it would be possible to free up even more RAM, which is something I needed to fit in all the parts that I hadn't touched yet. Guards and fighting logic, among other things.&lt;br /&gt;&lt;br /&gt;But most of all I was hoping that it would allow me to fit two full frame buffers into memory, to get rid of all the ugly redrawing glitches.&lt;br /&gt;So off I went, tearing my old code apart and rearranging it. I initially planned to use an Ocean cartridge but quickly found some inconsistencies in VICE's emulation of that type of cartridge, which meant that my code worked in VICE but didn't work on real hardware. I decided to continue working within VICE and got it up and running in a few days, after which I made a little video capture to show off the double-buffered drawing code.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/BHi5JgN8-2E/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/BHi5JgN8-2E?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/BHi5JgN8-2E?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now&amp;nbsp;I was finally able to add new features. I added Steve's player sprites and did the status display on the bottom. Things started to become easy again.&lt;br /&gt;&lt;br /&gt;Getting annoyed by Ocean cartridge limitations I decided to switch to &lt;a href="http://skoe.de/easyflash/"&gt;EasyFlash&lt;/a&gt;. It was a clean, well documented standard, with excellent support in VICE. I was amazed by enthusi's conversions of &lt;a href="http://noname.c64.org/csdb/release/?id=98674"&gt;Maniac Mansion and Zak McKracken&lt;/a&gt;&amp;nbsp;and&amp;nbsp;I liked the fact that the cartridge could be flashed using just a C64, eliminating the need for dodgy Windows software. Also the prize was affordable, so I ordered a pre-built cartridge and it arrived at the beginning of May.&amp;nbsp;I was very excited that I could finally play the cartridge-based build on real hardware so I made a little movie.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/VaoYGbvdgFc/0.jpg" height="480" width="292"&gt;&lt;param name="movie" value="http://www.youtube.com/v/VaoYGbvdgFc?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="480" height="292"  src="http://www.youtube.com/v/VaoYGbvdgFc?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;I still had a good chunk of work ahead of me. The most pressing issue now was to get the guards in, and to do a serious amount of performance optimizations.&lt;br /&gt;To help with the first I was lucky that Steve had already released his conversions of those animation frames. So I now had to figure out which part of the code was dealing with the second character.&lt;br /&gt;&lt;br /&gt;The basic idea of the game engine is that most of it can deal with either the Prince or a guard. The routines use the CharData structures, so the same animation code is used to animate them both. That gave me a hint that I should have almost all of the code already in place.&lt;br /&gt;I had to do a bit of cleaning up and structure it more like the original. The low-level functions for executing the sequence and looking up frame definitions were shared. The code calling those was split up into two parts:&amp;nbsp;updateKidAnimation and&amp;nbsp;updateShadAnimation.&lt;br /&gt;&lt;br /&gt;The first of the two loads KidData into CharData and ShadData into OppData using loadKidAndShad. It then runs the input control code (updateControlAndCheckForDeath), parses the sequence table (animChar), applies acceleration and velocity to the character, then updates the position. Afterwards it does collision detection and resolution.&lt;br /&gt;&lt;br /&gt;The other function, updateShadAnimation uses&amp;nbsp;loadShadAndKid to load ShadData into CharData and KidData into OppData, but otherwise does many of the same things. It obviously doesn't run the input code but instead it checks the type of opponent and then runs&amp;nbsp;updateOpponent which handles hard-wired behaviors of mouse, skeleton and shadow man and generic sword fighting movement for guards (updateNormalGuardImpl). It then goes through much of the same code as updateKidAnimation to animate the character&amp;nbsp;and update its position.&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Since the guards only have a small subset of animation frames compared to the Prince, they also only have a smaller frame def list. To deal with this, readFrameDef calls a routine which I called&amp;nbsp;overrideFrameDefListForCharacter. It checks CharID and then reads from the alternative frame def list via&amp;nbsp;indexIntoGuardFrameDefList.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;After&amp;nbsp;updateCharacters is done with dealing with the Kid and the Shad separately it handles common code needed for fighting (checkIfStabbingOrBlocking,&amp;nbsp;checkIfCharacterIsHit,&amp;nbsp;checkIfStabbingOpponent) before updating the strength of both characters. Now all the state variables of the characters are final and the game is ready to update the screen.&lt;br /&gt;&lt;br /&gt;Having made many mistakes in reverse engineering all of this code at first, I was quite happy to finally see a second animated character on the screen. Initially the fighting code didn't quite work, but that also came together after a few days. I had reached the point now where I had to move some of the code to ROM, and the guard AI logic was the prime candidate for that. So I still had room to grow the game.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-WVOejyvjTDM/Tt_mqMX9t2I/AAAAAAAAAIo/jEJYV1rdmn4/s1600/pop_120511.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-WVOejyvjTDM/Tt_mqMX9t2I/AAAAAAAAAIo/jEJYV1rdmn4/s1600/pop_120511.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;The skeleton is just a normal guard that can't be killed&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;By now I had already became a bit bored of the first level in the game. So I added the blueprint data for all 14 levels, also because I wanted to see the skeleton in level 3. It turned out really nicely.&lt;br /&gt;&lt;br /&gt;Within a month of rebooting the project, I had made a lot of progress and was confident that I could finish this game. But because I was unsure about the remaining parts I still expected it to take another year. The requirements for cutscenes and title screen scared me a bit. I had no idea how much work that would be. Also I had no music or sound effects, no final background graphics and performance was still abysmal. The latter now became a real problem. I had to tackle that one first. But I was just about to leave for vacation to Barcelona. Would that be a problem or a blessing? Find out in the next part.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-4010416027786904742?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/4010416027786904742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/12/part-eight-everything-comes-to-him-who.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/4010416027786904742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/4010416027786904742'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/12/part-eight-everything-comes-to-him-who.html' title='Part Eight - Everything comes to him who waits'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-WVOejyvjTDM/Tt_mqMX9t2I/AAAAAAAAAIo/jEJYV1rdmn4/s72-c/pop_120511.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-4567989875647629728</id><published>2011-11-09T13:31:00.000-08:00</published><updated>2011-11-09T13:32:02.117-08:00</updated><title type='text'>Part Seven - Hitting the memory and motivation barrier</title><content type='html'>Throughout September and October of 2009 I worked tirelessly to implement all the various gameplay features. I went through all the transitional objects and implemented them one by one.&lt;br /&gt;Unfortunately I quickly ran into problems due to the fact that I didn't have a double-buffered drawing system.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/90iD64NuIWg/0.jpg" height="340" width="416"&gt;&lt;param name="movie" value="http://www.youtube.com/v/90iD64NuIWg?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="416" height="340"  src="http://www.youtube.com/v/90iD64NuIWg?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;This video was captured on 25th October 2009, and it shows that basically all of the gameplay features work now. But there's horrible ugly flashing artifacts and generally the performance is rather bad.&lt;br /&gt;&lt;br /&gt;I did try various ways to see if I could hide the drawing artifacts, by not wiping the blocks that are redrawn, but rather redrawing them in a way that will clear the previously drawn pixels. This works a bit for the gates (except in the beginning, when they still overlap with the floor), but in general I had to come to the conclusion that this is not a solution for everything.&lt;br /&gt;But I hadn't given up yet.&lt;br /&gt;&lt;br /&gt;Then something interesting happened. On 4th November 2009, STE'86 (Steven Day) of Compunet fame posted on the Lemon64 forum that he was working on converting the Prince of Persia animations to C64 sprites:&amp;nbsp;&lt;a href="http://www.lemon64.com/forum/viewtopic.php?t=31893"&gt;http://www.lemon64.com/forum/viewtopic.php?t=31893&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;People showed some interest, including&amp;nbsp;Poharai Attila, who did the old preview for the C64 that I mentioned in my first post. I was still a long way from finishing my version, but I clearly had progressed far. So I panicked. I had to announce what I was doing to make sure nobody else spends a lot of time getting to where I am now only to find out that was much further along. I knew that this would be a serious bummer. :)&lt;br /&gt;&lt;br /&gt;So I made a post stating that I'm working on Prince of Persia, but with the intention of not letting myself get dragged into a long discussion that would divert my time from the project. But some people reacted positively and that felt good. I sent a few screenshots and movies to STE'86 and his partner in crime, JCB (PeteD), to prove to them that I've reached a playable prototype stage.&lt;br /&gt;&lt;br /&gt;At this point I was still convinced that the REU as a requirement would be enough to fit the game into memory. I considered a cartridge-based solution as an alternative for people without REU but didn't know any details about the cartridge technology, so I only considered it as something for later.&lt;br /&gt;To give you an idea, this is what my memory map looked like in November of 2009:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;; Runtime memory:&lt;br /&gt;;&lt;br /&gt;; $0000-$03FF : Game state + various buffers&lt;br /&gt;; $0400-$3FFF : Code&lt;br /&gt;; $4000-$5FFF : Bitmap&lt;br /&gt;; $6000-$63FF : Screen + Sprite pointers&lt;br /&gt;; $6400-$6FFF : Sprite buffers&lt;br /&gt;; $7000-$79FF : Seqtable&lt;br /&gt;; $7A00-$7EAF : Framedef list&lt;br /&gt;; $7EB0-$7FFF : Tables 2&lt;br /&gt;; $8000-$A80B : Background imagetable&lt;br /&gt;; $A80C-$AFFF : FREE (to be used by additional bg images in palace levels)&lt;br /&gt;; $B000-$B5FF : Tables 3&lt;br /&gt;; $BC00-$BDFD : Kid imagetable header (pointers into REU where to get each image from)&lt;br /&gt;; $BDFE-$BFFF : Kid image buffer&lt;br /&gt;; $C000-$DFFF : Mask bitmap&lt;br /&gt;; $E000-$EBFF : FREE =&amp;gt; music/sfx&lt;br /&gt;; $EC00-$F4FF : Level blueprint&lt;br /&gt;; $F500-$FFE8 : Tables&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I did put all the Kid images into the REU (pre-mirrored) and DMA-ed them in when needed. Doing the same for background images turned out to be prohibitively slow when redrawing, so I decided to keep them in memory. &lt;br /&gt;The size of the code had almost reached the $4000 barrier, but I didn't have any guard AI or sword fighting in there yet. &lt;br /&gt;And then of course I would need another set of bitmap and screen for the double buffering. &lt;br /&gt;&lt;br /&gt;I spent some time to auto-convert the images for a guard, and added those to the REU memory too, but I didn't get anywhere with the code, so I didn't really use them in game yet. I spent some time moving things around and changing the memory layout, and many of the more daring changes would've required me to rewrite a lot of the existing masking code. It started to get annoying.&lt;br /&gt;&lt;br /&gt;As it turned out, I had drained my motivation reservoir. By now I had worked six months on this project. While I made some progress, I had no real plan for how to finish it. Having announced it also took a huge chunk out of my desire to further spend time working on this game.&lt;br /&gt;&lt;br /&gt;So I slowly stopped working on Prince of Persia. I kept thinking about possible ways to solve my issues, but soon enough I was getting interested in other things and my mind was no longer on the case. It would take 17 months for me to get back to that project.&lt;br /&gt;&lt;br /&gt;I leave you this time with the thing that I've promised at the beginning. All my reverse-engineering work of the Apple II version, labels and disassembler output. It's not 100% complete, since I didn't fully investigate the low-level Apple II bits that I didn't need to understand, but it's more than enough to document the high-level logic and game structure which makes it possible to port it to another platform. &lt;br /&gt;You can &lt;a href="http://www.twinbirds.com/prince_of_persia/prince_of_persia_apple2_disasm.zip"&gt;download the package&lt;/a&gt; and get cracking, but please do read the readme.txt file first.&lt;br /&gt;&lt;br /&gt;The next time you'll learn how I re-booted the project and what happened next.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-4567989875647629728?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/4567989875647629728/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/11/part-seven-hitting-memory-and.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/4567989875647629728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/4567989875647629728'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/11/part-seven-hitting-memory-and.html' title='Part Seven - Hitting the memory and motivation barrier'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-9126044243911528111</id><published>2011-10-29T10:45:00.000-07:00</published><updated>2011-10-29T10:48:07.922-07:00</updated><title type='text'>Part Six - Playing hide and seek with pixels</title><content type='html'>At the beginning of August 2009, I had already progressed quite far. In the previous two months I got to the point where I could run with the Prince through the first level, except that none of the interactive elements of the game worked yet.&lt;br /&gt;And there was one big visual problem. Walking behind the pillars in the level didn't cause the character to be occluded. The prince was simply a set of sprites on top of the level background graphics. Not good enough.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;In the Apple II original (and almost all other versions of the game) this issue is solved easily because the Kid is drawn into the same frame buffer as the level graphics. Then afterwards the occluding front pieces of each block are simply drawn again, overdrawing the parts of the player that should be invisible. It's as common a technique as possible.&lt;br /&gt;But since the C64 has a color attribute system where you can only specify colors for each 8x8 block and not per pixel, using the same kind of drawing system would've caused the player to look very weird. The colors used for floors and other nearby parts would bleed into the player image. A problem which is commonly known as "color clash". Look no further than the ZX Spectrum version, which suffers from this problem, albeit they did try to hide it as much as possible.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-FKIMbtVCxzc/TqsJp1FrqpI/AAAAAAAAAGA/pcjR1ZmV9dA/s1600/color_clash.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-FKIMbtVCxzc/TqsJp1FrqpI/AAAAAAAAAGA/pcjR1ZmV9dA/s1600/color_clash.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;On the ZX Spectrum, the colors of the torch bleed into the image of the character&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;I knew that I definitely wanted to avoid this problem on the C64. That's why I initially set out to use sprites, which have their own set of colors and therefore won't clash with the background colors.&lt;br /&gt;&lt;br /&gt;Now generally you would solve the visibility of a sprite on the C64 using the sprite/background priority system. The VIC-II chip considers two of the 4 possible color bit-pairs as background and the other two as foreground. Each sprite can be selected to be either above foreground and background, or between foreground and background. Unfortunately this means that the foreground colors (bit pairs 10 and 11) are always drawn on top of sprites, so it's not possible to fully occlude a sprite with all of the possible colors.&lt;br /&gt;&lt;br /&gt;The other method typically used to obscure certain parts of a sprite is by overlaying them with another sprite that has a higher priority and contains the same graphics as the background. This however requires that sprite to share some of its colors with the background, which puts a restriction on the colors to use for the actual sprite (two out of three sprite colors are shared).&lt;br /&gt;Another trick is to put one sprite behind the foreground layer and the lower priority sprite in front of the foreground layer. This will perform the same masking but the foreground graphics will hide the sprite that is causing the masking.&lt;br /&gt;But I knew already from the size of the animation images that I'll need a lot of sprites and that using extra sprites for masking would be pretty much impossible. Plus that would've meant even higher memory usage.&lt;br /&gt;&lt;br /&gt;After considering all of these options and also taking their complexities into account, I've decided for the one solution that would be slowest, but would yield the most accurate results: Actually modifying the sprite data in memory, for each frame, every time. Ouch. After all, I wanted to have pixel-precise occlusion.&lt;br /&gt;So whenever the character would walk behind a pillar or other occluding object, I would have to mask out those pixels of the sprites and replace them with the transparent color bit-pair.&lt;br /&gt;I was confident that I could optimize the code to the point where it would be fast enough.&lt;br /&gt;&lt;br /&gt;To simplify the masking code, I decided to use a whole bitmap (8KB of data) to buffer the mask for the whole screen. I thought that it should be easy to piggyback the drawing of the mask onto the normal drawing code, which I already was familiar with.&lt;br /&gt;I basically just modified the function that draws the front piece images (182F:drawFrontPieceImage) to also draw into the mask bitmap, and added a simple set of images to be used as masks.&lt;br /&gt;The result looked like this:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-EVDVLSuWlkE/TqsVGmcG33I/AAAAAAAAAGI/UaITBqYkooU/s1600/mask_buffer.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-EVDVLSuWlkE/TqsVGmcG33I/AAAAAAAAAGI/UaITBqYkooU/s1600/mask_buffer.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;The mask bitmap for the first screen of the game. Just think of it as a 1-bit depth buffer&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;The black parts of the image are pixels where the sprites of the Prince should be invisible. Basically using an AND operation of this bitmap with the sprite data would mask out the occluded parts.&lt;br /&gt;&lt;br /&gt;I probably went through 6 or 7 different versions of this masking code. In the beginning it was very unoptimized and therefore really slow. One of the reasons why it was so CPU intensive was the fact that the mask data needed to be shifted to align with the sprite data, depending on the X coordinate of the character on screen.&lt;br /&gt;&lt;br /&gt;It took a lot of work, but in the end I was quite happy with the performance, but I'm sure someone is going to find some cycles to save to make it even faster. I have specialized the routines for 2, 3, 4, 5 and 6 byte-wide character images, here's an excerpt that performs reading of the mask, shifting and anding for one pixel row of a sprite that contains a two-byte wide image:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;            ; get branch offset for shift array&lt;br /&gt;            ldy ImageXOffset&lt;br /&gt;            lda BlockOffsetToShiftBranchOffset2,y&lt;br /&gt;            sta ShiftBranchOffset&lt;br /&gt;&lt;br /&gt;            ; read 3 bytes from mask&lt;br /&gt;            ldy #$00&lt;br /&gt;            lda (MaskBitmapPtr),y&lt;br /&gt;            sta CurrentMask&lt;br /&gt;            &lt;br /&gt;            ldy #$08&lt;br /&gt;            lda (MaskBitmapPtr),y&lt;br /&gt;            sta CurrentMask+1&lt;br /&gt;&lt;br /&gt;            ldy #$10&lt;br /&gt;            lda (MaskBitmapPtr),y&lt;br /&gt;            sta CurrentMask+2&lt;br /&gt;&lt;br /&gt;            ; shift the mask to align with the sprite&lt;br /&gt;            clv&lt;br /&gt;ShiftBranchOffset = *+1&lt;br /&gt;            bvc ShiftBranchOffset&lt;br /&gt;            asl CurrentMask+2&lt;br /&gt;            rol CurrentMask+1&lt;br /&gt;            rol CurrentMask+0&lt;br /&gt;            asl CurrentMask+2&lt;br /&gt;            rol CurrentMask+1&lt;br /&gt;            rol CurrentMask+0&lt;br /&gt;            asl CurrentMask+2&lt;br /&gt;            rol CurrentMask+1&lt;br /&gt;            rol CurrentMask+0&lt;br /&gt;            asl CurrentMask+2&lt;br /&gt;            rol CurrentMask+1&lt;br /&gt;            rol CurrentMask+0&lt;br /&gt;            asl CurrentMask+2&lt;br /&gt;            rol CurrentMask+1&lt;br /&gt;            rol CurrentMask+0&lt;br /&gt;            asl CurrentMask+2&lt;br /&gt;            rol CurrentMask+1&lt;br /&gt;            rol CurrentMask+0&lt;br /&gt;            asl CurrentMask+2&lt;br /&gt;            rol CurrentMask+1&lt;br /&gt;            rol CurrentMask+0&lt;br /&gt;            asl CurrentMask+2&lt;br /&gt;            rol CurrentMask+1&lt;br /&gt;            rol CurrentMask+0&lt;br /&gt;&lt;br /&gt;            txa&lt;br /&gt;            tay&lt;br /&gt;            &lt;br /&gt;            ; mask and store the pixel row&lt;br /&gt;            lda CurrentMask&lt;br /&gt;            and (RomSpriteImagePtr),y&lt;br /&gt;            sta (SpriteImagePtr),y&lt;br /&gt;&lt;br /&gt;            iny&lt;br /&gt;&lt;br /&gt;            lda CurrentMask+1&lt;br /&gt;            and (RomSpriteImagePtr),y&lt;br /&gt;            sta (SpriteImagePtr),y&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The right amount of shift operations is performed by modifying the parameter of the branch instruction. Three mask bytes are required temporarily, even though only two will be used to actually mask the sprite data afterwards.&lt;br /&gt;&lt;br /&gt;Here's an early version of the masking code in action, a video I captured on August 9th, 2009:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/FjlaGpeMRW4/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/FjlaGpeMRW4?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/FjlaGpeMRW4?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;You can see that the masking is not really precise yet (there's a one pixel offset, because the sprites are positioned on the full 320 pixel horizontal grid), and that it gets quite choppy when the player image is at its maximum width during a jump.&lt;br /&gt;&lt;br /&gt;Download a &lt;a href="http://twinbirds.com/prince_of_persia/pop_090809.prg"&gt;C64 .prg of the build from 09-Aug-2009&lt;/a&gt;. Like the previous one, this will require a REU to run.&lt;br /&gt;&lt;br /&gt;I wanted to press on now, to get to the point where the level is fully playable. So I entered another long phase of reverse-engineering the disassembled code to understand the parts that deal with animating the dynamic blocks, such as torches, gates, spikes, etc.&lt;br /&gt;From reading the document, I saw that this was all a bit complicated. Starting on page 10 there's an explanation of the "redraw buffers". Those are 30 byte buffers, which contain a redraw count for each block on the screen. Typically these are filled with a value of 2 for a block to make sure it gets redrawn in both frame buffers (since the game uses double-buffering).&lt;br /&gt;It didn't take me very long to find the redraw buffers and the code that deals with them, but it did take quite a while to identify each buffer correctly, because they're all a bit similar:&lt;br /&gt;&lt;br /&gt;5E3C:HalfBuf&lt;br /&gt;5E5A:RedBuf&lt;br /&gt;5E78:FRedBuf&lt;br /&gt;5E96:FloorBuf&lt;br /&gt;5EB4:WipeBuf&lt;br /&gt;5ED2:MoveBuf&lt;br /&gt;5EF0:ObjBuf&lt;br /&gt;5F0E:WhiteBuf&lt;br /&gt;5F2C:TopBuf&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The simplest redraw buffer is RedBuf. Setting an entry for a block in RedBuf causes the whole block to be redrawn. To get a correct redraw it's often necessary to clear the block first, this is done by using WipeBuf with a certain wipe height (which is stored in WhiteBuf). So, for example, a wipe height of $3f (63 pixels) would wipe the whole block. The reference point is the bottom of the block.&lt;/div&gt;&lt;div&gt;So typically a redraw of a block is triggered by both storing a value in WipeBuf and RedBuf:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;            lda #$3f&lt;br /&gt;            sta ImageTableEntry     ; ImageTableEntry is used for wipe height and stored in WhiteBuf&lt;br /&gt;&lt;br /&gt;            ; Get the current position of the character and &lt;br /&gt;            ; convert it to a block index in Y&lt;br /&gt;            jsr convertCharXAndYToBlockIndexInY  &lt;br /&gt;&lt;br /&gt;            lda #$02&lt;br /&gt;            jsr storeRedBufValue    ; store into RedBuf at offset Y&lt;br /&gt;            jsr storeWipeBufValue   ; store into WipeBuf at offset Y&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;It doesn't matter if storeWipBufValue is called after storeRedBufValue, since the order of performing the redraws is fixed, and the wipe will always come before the redraw.&lt;br /&gt;&lt;br /&gt;In the end there's one function dealing with applying the redraw buffers and it's called for every block from an iteration loop that is very much like iterateScreen:&lt;/div&gt;&lt;br /&gt;&lt;div&gt;1293:iterateScreenForRedraw&lt;br /&gt;164B:redrawBlock&lt;/div&gt;&lt;br /&gt;&lt;div&gt;So as expected, redrawBlock has a certain fixed order in which it handles all of the redraw buffers. Wipes come first, then normal redraws, and so on until the last buffer, which is FRedBuf, for redrawing front pieces:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;;----------------------------------&lt;br /&gt;redrawBlock:&lt;br /&gt;            lda WipeBuf,y&lt;br /&gt;            beq noWipeNeeded&lt;br /&gt;&lt;br /&gt;            sec&lt;br /&gt;            sbc #$01&lt;br /&gt;            sta WipeBuf,y&lt;br /&gt;&lt;br /&gt;            jsr wipeBlock&lt;br /&gt;            ldy ScreenUpdateBlockIndex&lt;br /&gt;&lt;br /&gt;noWipeNeeded:&lt;br /&gt;&lt;br /&gt;            lda RedBuf,y&lt;br /&gt;            beq noRedrawNeeded&lt;br /&gt;&lt;br /&gt;            sec&lt;br /&gt;            sbc #$01&lt;br /&gt;            sta RedBuf,y&lt;br /&gt;&lt;br /&gt;            jsr switchToBgImageList&lt;br /&gt;            jsr drawBlock&lt;br /&gt;            ldy ScreenUpdateBlockIndex&lt;br /&gt;&lt;br /&gt;noRedrawNeeded:&lt;br /&gt;&lt;br /&gt;            [...]&lt;br /&gt;&lt;br /&gt;            lda FRedBuf,y&lt;br /&gt;            beq noFrontPieceRedrawNeeded&lt;br /&gt;&lt;br /&gt;            sec&lt;br /&gt;            sbc #$01&lt;br /&gt;            sta FRedBuf,y&lt;br /&gt;&lt;br /&gt;            jsr drawFrontPieceImage&lt;br /&gt;            ldy ScreenUpdateBlockIndex&lt;br /&gt;&lt;br /&gt;noFrontPieceRedrawNeeded:&lt;br /&gt;            rts&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Oh it looks quite simple now, but that took me a while. Many wrong leads that caused me to go off in the wrong direction, but thankfully redrawing calls the same basic drawing functions like drawBlock which I already knew from drawing the initial screen, so that helped in understanding how it all works together.&lt;br /&gt;&lt;br /&gt;So as soon as I put in working versions of redrawBlock and iterateScreenForRedraw, I was able to add the code that stores values into the redraw buffers. The first one I tried were the torches.&lt;br /&gt;Those are "transitional objects", so they use MoveBuf to redraw themselves. Most of the trans objects have specialized drawing code, e.g. the gate updates itself by drawing a tiled sequence of gate parts, depending on how high the gate currently is raised. The code for drawing the torches is specialized to only draw the flame itself, skipping the rest of the block.&lt;br /&gt;&lt;br /&gt;The starting point for the trans object system is this function:&lt;br /&gt;&lt;br /&gt;EE00:animateTransObjs&lt;br /&gt;&lt;br /&gt;For each active trans object, it maintains the movement direction (e.g. for gates and spikes), the block index and the screen number, and then calls animateTransObj to update it.&lt;br /&gt;&lt;br /&gt;F0FE:animateTransObj&lt;br /&gt;&lt;br /&gt;Depending on the object id, this function dispatches to one of many different individual ones:&lt;br /&gt;&lt;br /&gt;F194:animateExitTransObj&lt;br /&gt;F1EC:animateGateTransObj&lt;br /&gt;F264:animatePressPlateTransObj&lt;br /&gt;F287:animateJawTransObj&lt;br /&gt;F2DF:animateFlaskTransObj&lt;br /&gt;F302:animateSwordTransObj&lt;br /&gt;F31B:animateTorchTransObj&lt;br /&gt;F354:animateSpikesTransObj&lt;br /&gt;F386:animateLooseFloorTransObj&lt;br /&gt;&lt;br /&gt;Their main job is to update the BlueSpec value for their block and store into the redraw buffers to make sure they're updated during the next redraw loop.&lt;br /&gt;&lt;br /&gt;It is now late August 2009. I had various redraws working, and I started to also set video and color RAM differently for each block, to introduce a bit of color into my screens. Due to many changes some of the drawing was often broken, so the versions from this time were not really playable. But here's how it looked like with some early flame animations in place:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-eo5h1fd3LxI/TqwuQQY3jLI/AAAAAAAAAGQ/TNMY7OgKy6U/s1600/flames.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-eo5h1fd3LxI/TqwuQQY3jLI/AAAAAAAAAGQ/TNMY7OgKy6U/s1600/flames.gif" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Some colors to make the new torch flame animations a bit prettier&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;So that's it for this time. In the next part we're gonna look at bit more at the redrawing of gates and spikes, and why I was suddenly forced to reveal my project to the public.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-9126044243911528111?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/9126044243911528111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/10/part-six-playing-hide-and-seek-with.html#comment-form' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/9126044243911528111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/9126044243911528111'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/10/part-six-playing-hide-and-seek-with.html' title='Part Six - Playing hide and seek with pixels'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-FKIMbtVCxzc/TqsJp1FrqpI/AAAAAAAAAGA/pcjR1ZmV9dA/s72-c/color_clash.png' height='72' width='72'/><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-716356440079851257</id><published>2011-10-24T14:30:00.000-07:00</published><updated>2011-10-24T14:30:35.202-07:00</updated><title type='text'>Part Five - Painting a pretty dungeon</title><content type='html'>It's now late July 2009, and I finally had to really start looking into that interesting drawing that Jordan put in his document:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-2K2mV0_zH0A/TqUGSEXWNFI/AAAAAAAAAFY/YXxZoEV5aIg/s1600/drawblock.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="343" src="http://3.bp.blogspot.com/-2K2mV0_zH0A/TqUGSEXWNFI/AAAAAAAAAFY/YXxZoEV5aIg/s400/drawblock.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Jordan Mechner's sketch of the sections of a block and their drawing order&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;So it seemed that every block is drawn by first drawing the things that it overlaps with. The C-section from the block left below, and the B-section of the block to the left. After that it draws the actual visible parts of that block, divided into D-section and A-section, where the D-section is just the part that would be visible if the block was one of those top row blocks, i.e. just 3 pixels high. I didn't yet know what the "frontpiece" was, but it seemed like it must be related with making sure the players goes behind certain parts of the block.&lt;br /&gt;&lt;br /&gt;Here's a slow motion version of the drawing loop, building up the whole screen with the painter's algorithm for each block:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-fJ3EbSQm-Tw/TqXNOUgrSLI/AAAAAAAAAFw/6f3_BP9Dnzc/s1600/draw2.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-fJ3EbSQm-Tw/TqXNOUgrSLI/AAAAAAAAAFw/6f3_BP9Dnzc/s1600/draw2.gif" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Drawing the first screen in the game, a process which is normally not visible&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;At this point, I looked into the individual block images that I had extracted from the PC level graphics (VDUNGEON.DAT) using PR. And sure enough, those were also divided into those different sections. What I've noticed was that the bitmaps for each section where only as large as they needed to be, i.e. the B-section of most blocks didn't reach all the way to the bottom of the block, and the C-Section often was only as wide as the visible parts that it covered. Here's an example:&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-LR0AsZN1geM/TqXJ6-Gza5I/AAAAAAAAAFo/cqLMsYilHmI/s1600/block_sections.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-LR0AsZN1geM/TqXJ6-Gza5I/AAAAAAAAAFo/cqLMsYilHmI/s1600/block_sections.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Sections of a block, and the actual sizes of the bitmaps in the DOS version&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;So I had two things on my todo list now. First converting all the DOS VGA images to C64 colors and resolution. For that I used the same method that I applied to the character animation frames, but this time I had to do more manual fixing, because it wasn't possible to use a simple color mapping.&lt;br /&gt;The other thing was to identify all of the images correctly. The data extracted from PR had nice names, but it wasn't always clear what each block was. There were some obvious ones, but most of the parts used for floors and pressure plates were ambiguous. I needed to clearly map them to the correct image, preferably also in the right order. My main guide there was the list on page 23 of the PDF, which lists all the individual images and clearly marks them as A, B, C or D. Turned out that this list was absolutely invaluable.&lt;br /&gt;&lt;br /&gt;So I had that list of image names from the Apple II version, and a folder containing images from the PC version. How could I make the connection between the two? Furthermore, it seemed like some of them didn't match up, e.g. there were a lot more images for wall blocks that didn't make sense. It felt like that the data was similar, but not exactly the same. I needed to really look at the Apple II images. I had to extract them.&lt;br /&gt;&lt;br /&gt;Fortunately, the page before the background image list in the document describes the data structure of those image tables. And with a bit of hunting in the disassembled code, I managed to find the locations of them in the memory snapshot.&lt;br /&gt;&lt;br /&gt;6000:BgTab1&lt;br /&gt;8400:BgTab2&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At this point I have to put my "Ode to &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt;". I use it to quickly generate one-off scripts to perform a simple task, like data conversion or extraction. I love doing that, it's fast and I get my results in a very short time. Python rules! :)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You can download the &lt;a href="http://twinbirds.com/prince_of_persia/pop_apple_imagetables/dungeon/"&gt;extracted image tables&lt;/a&gt; if you're interested. They're single-color Apple II images, with the MSB stripped, so they're nicer to look at.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;Here's the same block as above, this time taken from the Apple II version, with its width of 28 pixels per block, compared to 32 pixels on the PC.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-cRloCm33xfU/TqXVo0Q58WI/AAAAAAAAAF4/-Jp4AJJB_O4/s1600/block_sections_apple.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-cRloCm33xfU/TqXVo0Q58WI/AAAAAAAAAF4/-Jp4AJJB_O4/s1600/block_sections_apple.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Apple II block sections of the "pillars" block&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now I had a definitive list of images and their names as given by "the Book of Jordan". This allowed me to match them to the converted PC images I already had, and it also helped me to understand the differences. I knew that I had to match that image list with my own bitmap files and if I did then it should be fine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the previous part I mentioned the core of the iterateScreen loop, a routine called drawBlock. Here's what it looked like to me, after I figured out its constituent parts:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;drawBlock:&lt;br /&gt;            jsr drawCSectionLeftBelowStencil&lt;br /&gt;            jsr drawCSectionLeftBelowImage&lt;br /&gt;            jsr drawBSectionLeftImage&lt;br /&gt;            jsr drawTransitionalObjImage&lt;br /&gt;            jsr drawDSectionImage&lt;br /&gt;            jsr drawDSectionIfLooseFloor&lt;br /&gt;            jsr drawASectionImage&lt;br /&gt;            jsr drawTransitionalObjASection&lt;br /&gt;            jmp drawFrontPieceImage&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And it has a little brother, for drawing the top row blocks (which has not A-section):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;drawTopBlock:&lt;br /&gt;            jsr drawCSectionLeftBelowStencil&lt;br /&gt;            jsr drawCSectionLeftBelowImage&lt;br /&gt;            jsr drawBSectionLeftImage&lt;br /&gt;            jsr drawDSectionImage&lt;br /&gt;            jsr drawDSectionIfLooseFloor&lt;br /&gt;            jmp drawFrontPieceImage&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There are a bunch of ZP memory locations used as input to these functions:&lt;br /&gt;&lt;br /&gt;0029:ScreenUpdateLeftObjId&lt;br /&gt;002A:ScreenUpdateLeftBlueSpec&lt;br /&gt;00E9:ScreenUpdateRow&lt;br /&gt;00EA:ScreenUpdateColumn&lt;br /&gt;00EB:ScreenUpdateBlockIndex&lt;br /&gt;00EC:ScreenUpdateObjId&lt;br /&gt;00ED:ScreenUpdateBlueSpec&lt;br /&gt;00EE:ScreenUpdateYCoordMinus3&lt;br /&gt;00EF:ScreenUpdateYCoord&lt;br /&gt;00F3:ScreenUpdateXCoordInBytes&lt;br /&gt;&lt;br /&gt;Their job is to setup the actual low-level bitmap drawing code. But as described in the source code documentation (page 9 in the PDF), these hires routines are never called directly. Instead there's an extra indirection layer, called "image lists".&lt;br /&gt;Basically every draw operation is added to one of the image lists, which are then executed in one step:&lt;br /&gt;&lt;br /&gt;0403:executeImageLists&lt;br /&gt;&lt;br /&gt;Here I decided to take a shortcut. The requirement for deferring every drawing operation to happen at a later point seemed unnecessary and slow to me. I took a bit of a gamble and used the functions that add to the image list as my interface to my own low-level drawing code.&lt;br /&gt;I started to implement &lt;br /&gt;&lt;br /&gt;1A5F:addToImageList&lt;br /&gt;&lt;br /&gt;to do my bitmap blitting immediately to the screen.&lt;br /&gt;The information which image to draw (or in the original: which one to add to the image list) is again passed in a set of ZP variables (as explained on page 10 of the PDF):&lt;br /&gt;&lt;br /&gt;0001:ImageXPosInBytes&lt;br /&gt;0002:ImageYPos&lt;br /&gt;0003:ImageXOffset&lt;br /&gt;0004:ImageTableEntry&lt;br /&gt;0005:ImageWidthInBytes&lt;br /&gt;0006:ImageOpacity&lt;br /&gt;0007:ImageTableLo&lt;br /&gt;0008:ImageTableHi&lt;br /&gt;000F:ImageTopCutoff&lt;br /&gt;0010:ImageLeftCutoff&lt;br /&gt;0011:ImageRightCutoff&lt;br /&gt;0012:ImageTableBank&lt;br /&gt;0013:ImageBottomCutoff&lt;br /&gt;&lt;br /&gt;I wrote a function that takes these parameters and draws my own bitmaps. The ImageOpacity one is especially interesting. It's not documented in detail, but I managed to find out that it basically describes the blitting operation:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;IMAGE_OP_AND  = $00&lt;br /&gt;IMAGE_OP_ORA  = $01&lt;br /&gt;IMAGE_OP_STA  = $02&lt;br /&gt;IMAGE_OP_EOR  = $03&lt;br /&gt;IMAGE_OP_AND2 = $04&lt;br /&gt;IMAGE_OP_STA2 = $05  ; unused&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;On the Apple II, this variable is directly used to modify the opcode in the inner blitting loop. It's using AND to draw a stencil with a mask bitmap, ORA to draw blocks with 0 bits being transparent, STA to simply overwrite, and EOR to invert.&lt;br /&gt;After a lot of failed attempts, I finally got something on the screen. And since I already had all the animation system up and running, and had correctly added the code to trigger the screen drawing, I very quickly got to a point where I could run through the level. Lots of little problems, but the result was undeniably working. Although since animated objects didn't work yet, gates didn't open and loose floors didn't break, I wasn't able leave the first room. I decided to move the starting position to a different screen, to be able to roam around.&lt;br /&gt;&lt;br /&gt;But there's one thing I didn't mention yet. Getting to this point wasn't really doable with 64K of RAM, not with this approach and my unoptimized code. So I decided to use a REU (Ram Expansion Unit). I added code to upload all the images and DMA in the data I needed to draw on demand. I didn't know if that would be a workable solution for the final game, but it allowed me to progress, which was the most important thing at that moment: To push forward!&lt;br /&gt;&lt;br /&gt;Here's a little video of the build from July 28th 2009:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/lAv_hoFdroE/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/lAv_hoFdroE?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/lAv_hoFdroE?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;You can also download a &lt;a href="http://twinbirds.com/prince_of_persia/pop_280709.prg"&gt;C64 .prg file of this build&lt;/a&gt;, but please be aware that it will require a REU and it has not been tested on a real C64, so your mileage may vary.&lt;br /&gt;&lt;br /&gt;Next time I'm gonna tell a bit about masking and redrawing. Sounds boring, but is essential in order to make the Prince and his environment feel real. Oh, and it's a real pain in the ass, too! :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-716356440079851257?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/716356440079851257/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/10/part-five-painting-pretty-dungeon.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/716356440079851257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/716356440079851257'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/10/part-five-painting-pretty-dungeon.html' title='Part Five - Painting a pretty dungeon'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-2K2mV0_zH0A/TqUGSEXWNFI/AAAAAAAAAFY/YXxZoEV5aIg/s72-c/drawblock.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-4677695014559466910</id><published>2011-10-20T14:10:00.000-07:00</published><updated>2011-10-20T14:32:23.483-07:00</updated><title type='text'>Part Four - To bitmap, or not to bitmap, that is the question!</title><content type='html'>Now that I had a somewhat basic understanding of the way the game handles the player animations, controls and collision detection, there was a big unknown area left. How does it draw the background graphics? How does it handle the visibility of the Kid when he goes behind a pillar?&lt;br /&gt;&lt;br /&gt;Thankfully the document explains quite a bit about the drawing system.&lt;br /&gt;&lt;br /&gt;First, let's have a closer look at how the level data looks like. Each level consists of 24 screens, and each screen is divided into three rows of 10 blocks each. So 30 blocks in total per screen. Blocks can be identified by their X and Y coordinates or by their block index, e.g. the block in the bottom right corner has X/Y of 9/2 or a block index of 29 (we start to count at zero).&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-jeSvXNfXrfc/TqB-1cEOJVI/AAAAAAAAAFA/R6NiZRaiTpM/s1600/blockindex.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-jeSvXNfXrfc/TqB-1cEOJVI/AAAAAAAAAFA/R6NiZRaiTpM/s1600/blockindex.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Block indices&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;The data structure to describe the layout of a screen is simply 30 bytes, one for each block, containing a 5-bit object id number per block. The object ids were not listed in the document, but luckily someone else had already reverse-engineered them (see page 12 of &lt;a href="http://www.princed.org/content/files/documentation/FormatSpecifications.pdf"&gt;this document&lt;/a&gt;). I list them here for completeness sake with the names that Jordan used:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$00 = OBJID_EMPTY&lt;br /&gt;$01 = OBJID_FLOOR&lt;br /&gt;$02 = OBJID_SPIKES&lt;br /&gt;$03 = OBJID_POSTS&lt;br /&gt;$04 = OBJID_GATE&lt;br /&gt;$05 = OBJID_STUCK_PRESSPLATE&lt;br /&gt;$06 = OBJID_DOWN_PRESSPLATE&lt;br /&gt;$07 = OBJID_PANEL&lt;br /&gt;$08 = OBJID_PILLAR_BOTTOM&lt;br /&gt;$09 = OBJID_PILLAR_TOP&lt;br /&gt;$0a = OBJID_FLASK&lt;br /&gt;$0b = OBJID_LOOSE_FLOOR&lt;br /&gt;$0c = OBJID_PANEL_TOP&lt;br /&gt;$0d = OBJID_MIRROR&lt;br /&gt;$0e = OBJID_RUBBLE&lt;br /&gt;$0f = OBJID_UP_PRESSPLATE&lt;br /&gt;$10 = OBJID_EXIT1&lt;br /&gt;$11 = OBJID_EXIT2&lt;br /&gt;$12 = OBJID_JAW&lt;br /&gt;$13 = OBJID_TORCH&lt;br /&gt;$14 = OBJID_BLOCK&lt;br /&gt;$15 = OBJID_SKELETON&lt;br /&gt;$16 = OBJID_SWORD&lt;br /&gt;$17 = OBJID_BALCONY1&lt;br /&gt;$18 = OBJID_BALCONY2&lt;br /&gt;$19 = OBJID_ARCH_PILLAR&lt;br /&gt;$1a = OBJID_ARCH_SUPPORT&lt;br /&gt;$1b = OBJID_ARCH_SMALL&lt;br /&gt;$1c = OBJID_ARCH_TOP_LEFT&lt;br /&gt;$1d = OBJID_ARCH_TOP_RIGHT&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Here's the data describing the first screen of the game (note that the values need to be AND-ed with $1f to get object ids):&lt;/div&gt;&lt;pre&gt;$00 $00 $00 $21 $01 $21 $21 $21 $34 $34&lt;br /&gt;$33 $33 $21 $23 $00 $34 $14 $14 $14 $34&lt;br /&gt;$14 $14 $34 $34 $2E $23 $0B $01 $21 $34&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Interestingly, the PC version has an additional object id, even though the levels are basically identical to the Apple II version. That extra type is OBJID_TORCH_WITH_RUBBLE with id $1e.&lt;br /&gt;When a falling floor lands, it replaces the current block with OBJID_RUBBLE. But if that happens at a block of type OBJID_TORCH then the torch will suddenly disappear, because OBJID_RUBBLE does not include a torch. This bug has been fixed in the PC version (and probably others as well), where the block will be replaced with OBJID_TORCH_WITH_RUBBLE if it was OBJID_TORCH. &lt;br /&gt;&lt;br /&gt;So each screen is 30 bytes and there are 24 screens, which yields a total size of 720 bytes for the whole level layout. This part of the level data is called BlueType.&lt;br /&gt;There's a second set of 720 bytes per level (BlueSpec) which contains a state value byte for each block. Here the game keeps track of animation states (how far a gate has been raised, how far the spikes are extended, etc.)&lt;br /&gt;&lt;br /&gt;All of the above has been documented already, so nothing here was really new to me. But it didn't really help me with figuring out how to draw the screens yet. I had to do more code digging to find out.&lt;br /&gt;&lt;br /&gt;After having looked through the collision code earlier, I was able to identify a few crucial memory locations that deal with screens:&lt;br /&gt;&lt;br /&gt;00A6:NewVisScrn&lt;br /&gt;00CB:VisScrn&lt;br /&gt;0031:ScrnLeftOfVisScrn&lt;br /&gt;0032:ScrnRightOfVisScrn&lt;br /&gt;0033:ScrnAboveVisScrn&lt;br /&gt;0034:ScrnBelowVisScrn&lt;br /&gt;0035:ScrnLeftBelowOfVisScrn&lt;br /&gt;0036:ScrnLeftAboveOfVisScrn&lt;br /&gt;0037:ScrnRightAboveOfVisScrn&lt;br /&gt;0038:ScrnRightBelowOfVisScrn&lt;br /&gt;&lt;br /&gt;VisScrn is the currently visible screen number (1 to 24).&lt;br /&gt;If NewVisScrn is not equal to VisScrn, then it will cause a new screen to be drawn (after which VisScrn is set to NewVisScrn).&lt;br /&gt;There's also a routine I named &lt;br /&gt;&lt;br /&gt;D02A:getScrnsSurroundingVisScrn&lt;br /&gt;&lt;br /&gt;which fills in the other variables, the 8 screens surrounding the currently visible screen.&lt;br /&gt;&lt;br /&gt;I experimentally found out that it's easy to force the game to draw a new screen, by setting NewVisScreen to the desired screen number, but when doing that the Kid was still in the old room (i.e. collision detection was performed in that one), but I quickly noticed that I also had to change KidScrn ($5b) to the new screen to fully teleport the player.&lt;br /&gt;&lt;br /&gt;Now I was finally making a bit of progress. The code that changes NewVisScrn is triggered by&lt;br /&gt;&lt;br /&gt;59E0:cutCheck&lt;br /&gt;&lt;br /&gt;whose name was mentioned in the document. Basically the game checks if a character is exiting the screen (5B08:checkIfCharIsOffscreen) and changes CharScrn ($4b) if that's the case (5415:changeCharScrn). Then it sets NewVisScrn to CharScrn. &lt;br /&gt;&lt;br /&gt;After that&lt;br /&gt;&lt;br /&gt;24D3:checkIfScreenHasChangedAndNeedsToBeDrawn&lt;br /&gt;&lt;br /&gt;detects the screen change and sets ScreenHasNotBeenDrawn to 1, which indicates that the drawing code has to do a full screen refresh.&lt;br /&gt;&lt;br /&gt;At this point, I understood enough to really find the main update loop and make sense of it. This allowed me to make my own program structure to be more like the Apple II game.&lt;br /&gt;&lt;br /&gt;I identified mainLoop to be&lt;br /&gt;&lt;br /&gt;218C:mainLoop&lt;br /&gt;&lt;br /&gt;and with all final labels it looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;mainLoop:&lt;br /&gt;            jsr updateAndGetRandomNumber&lt;br /&gt;            lda #$00&lt;br /&gt;            sta KidStrengthDelta&lt;br /&gt;            sta ShadStrengthDelta&lt;br /&gt;            jsr updateInputDevices&lt;br /&gt;            jsr isButtonPressed&lt;br /&gt;            bpl l21a2&lt;br /&gt;&lt;br /&gt;            lda #$01&lt;br /&gt;            jmp initGameAndLevelImpl&lt;br /&gt;l21a2:&lt;br /&gt;            jsr updateTimers&lt;br /&gt;            jsr updateCharacters&lt;br /&gt;            jsr activateScreenFlash&lt;br /&gt;            jsr updateScreen&lt;br /&gt;            jsr updateSoundEffects&lt;br /&gt;            jsr clearSoundEffectBuffer&lt;br /&gt;            jsr updateScreenFlash&lt;br /&gt;            jsr updateMusic&lt;br /&gt;            lda NextLevel&lt;br /&gt;            cmp CurrentLevel&lt;br /&gt;            beq mainLoop&lt;br /&gt;startNextLevel:&lt;br /&gt;            jsr l27e5                         ; does something special before level 2&lt;br /&gt;            jmp activateNextLevelOrCutscene&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Back then I didn't know what most of these do, but updateScreen stood out because it did something with ScreenHasNotBeenDrawn. It checks if it's 0 or 1 and then branches, to either draw the whole screen (2439:initialDrawScreen) or just the parts that have changed in the current frame (2482:redrawScreen).&lt;br /&gt;Initially I had no idea what redrawScreen did. All I cared about was initialDrawScreen. I remember that at this point I didn't stop until I had all of its sub-routines documented. It turned out to be a pretty straight forward system. &lt;br /&gt;It basically scans through the blue print data of the screen (1290:iterateScreen) using the helper function (04CC:getScrnEntryInBluePrint) and calls (161E:drawBlock) for each block.&lt;br /&gt;&lt;br /&gt;At this point it really dawned on me that I will probably have to reproduce the whole screen drawing using a bitmap. Initially I still thought that I could probably use the old C64 shortcut of using a modified character set, which means to you only have to store one or two bytes to draw a whole 8x8 pixel block on the screen. But using bitmaps meant that I not only have to write 8 times as much data, also I'll need significantly more memory as frame buffer. And it was already tight.&lt;br /&gt;I finally noticed that the screen can not be nicely divided into 8x8 blocks. The height of one block row was not 64 pixels, but 63 pixels. This was because the top three pixels of the screen actually show the bottom 3 pixels of the screen above. That was something which was necessary for the player to see and break loose floors of that screen. &lt;br /&gt;Of course I could have fudged it a bit. Make each row 64 pixels high, draw the status display in the screen border using sprites, use the topmost 8 pixels for that special top row. Or maybe even force a badline at the right place to shorten one character row to 7 pixels.&lt;br /&gt;But what would the implications be? Would I have to change animation tables or hard-coded values to be able to climb and grab ledges if the screen rows are of a different height?&lt;br /&gt;How would I animate the dynamic parts of the screen? Could I fit it all into characters for every possible screen? I didn't want to rule out the possibility of using a level editor to create completely new levels.&lt;br /&gt;There were many things that I didn't know yet, so I decided to go for the safe route. Don't deviate from the original too much, until I know how it all works, and then reconsider. Better than painting myself into a corner by doing a premature optimization.&lt;br /&gt;&lt;br /&gt;So to this day, I still don't know if character mode would've been an option. I guess I'll have to wait for someone else to try it.&lt;br /&gt;&lt;br /&gt;Next time I'm gonna dive into how each block is actually drawn and how the game handles the isometric perspective and its visibility issues. And how I'm finally able to run through the first level.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-4677695014559466910?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/4677695014559466910/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/10/part-four-to-bitmap-or-not-to-bitmap.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/4677695014559466910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/4677695014559466910'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/10/part-four-to-bitmap-or-not-to-bitmap.html' title='Part Four - To bitmap, or not to bitmap, that is the question!'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-jeSvXNfXrfc/TqB-1cEOJVI/AAAAAAAAAFA/R6NiZRaiTpM/s72-c/blockindex.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-1626432740043200208</id><published>2011-10-18T14:34:00.000-07:00</published><updated>2011-10-18T14:36:24.910-07:00</updated><title type='text'>Part Three - Making the Kid come to life</title><content type='html'>After having successfully found my way into the animation code of Prince of Persia, my top priority was to take all of the information I gathered and integrate it into my current test program.&amp;nbsp;I decided that I'm just gonna take the relevant parts to be able to animate the character and make him run around the screen, with all the right actions, climbing, jumping, etc.&lt;br /&gt;So I had to completely dissect the code from the controls down to drawing the sprite onto the screen. I didn't plan to lose any time.&lt;br /&gt;&lt;br /&gt;The first thing I noticed from looking at the sequence table parsing were the zero-page locations that it's using. CHX is modifying $41, CHY is modifying $42 and so on. At the end of that code, if the byte from the sequence table is not any of the instructions, it does this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;l4a2a:&lt;br /&gt;                sta l40&lt;br /&gt;                rts&lt;/pre&gt;&lt;br /&gt;So if it's not an instruction, but a frame number, it gets stored directly into $40.&lt;br /&gt;Now it was very plain to see that these memory locations must be the "character data" variables mentioned on page 17 of the PDF.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Now was a good time to start creating a label file. This is a text file which contains a name for every memory location or code routine that has been identified. The disassembler will read it and start using those real names, instead of the generic address labels it uses by default. I also made the rule to use names from Jordan's document wherever possible, except when they were very non-descriptive.&lt;br /&gt;The development system that he used back in the days on the Apple II surely had a limit for the length of a label. Typically most of the assembler tools back then enforced labels to be no longer than 8 characters. This was necessary to save memory and keep source and object code in memory at the same time, at least as long as possible, to speed up the development cycle.&lt;br /&gt;Since I was using a modern cross-assembler (&lt;a href="http://k2devel.sourceforge.net/"&gt;k2asm&lt;/a&gt;) I had no such limitation, so I took the liberty to use longer names where appropriate.&lt;br /&gt;I also decided to rename "CharPosn" (which I've identified to be at $40 in memory) to CharFrame, because that seemed clearer to me. It's the index into the frame def list of the character, and represents the currently visible animation image.&lt;br /&gt;&lt;br /&gt;So these labels I was very confident about:&lt;br /&gt;&lt;br /&gt;0040:CharFrame&lt;br /&gt;0041:CharX&lt;br /&gt;0042:CharY&lt;br /&gt;0043:CharFace&lt;br /&gt;0044:CharBlockX&lt;br /&gt;0045:CharBlockY&lt;br /&gt;0046:CharAction&lt;br /&gt;0047:CharXVel&lt;br /&gt;0048:CharYVel&lt;br /&gt;0049:CharSeqLo&lt;br /&gt;004A:CharSeqHi&lt;br /&gt;004B:CharScrn&lt;br /&gt;004C:CharRepeat&lt;br /&gt;004D:CharID&lt;br /&gt;004E:CharSword&lt;br /&gt;004F:CharLife&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Together these 16 bytes make up the CharData. The document also mentions that there are multiple sets of CharData in the game: KidData (for the kid) and ShadData (for the opponent, which is always referred to as "Shad").&lt;/div&gt;&lt;div&gt;It describes the functions called LoadKid and SaveKid to load/save KidData to and from the working CharData set. These should be easy to find. And indeed there was a whole bunch of these routines:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;ld835:                   ; copy 16 bytes from $50 to $40&lt;br /&gt;            ldx #$0f&lt;br /&gt;ld837:&lt;br /&gt;            lda l50,x          &lt;br /&gt;            sta l40,x&lt;br /&gt;            dex&lt;br /&gt;            bpl ld837&lt;br /&gt;ld83e:&lt;br /&gt;            rts&lt;br /&gt;;----------------------------------&lt;br /&gt;ld83f:                   ; copy 16 bytes from $40 to $50&lt;br /&gt;            ldx #$0f&lt;br /&gt;ld841:&lt;br /&gt;            lda l40,x&lt;br /&gt;            sta l50,x&lt;br /&gt;            dex&lt;br /&gt;            bpl ld841&lt;br /&gt;ld848:&lt;br /&gt;            rts&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Copying 16 bytes from $50 to $40 and back, the same with $60 and $40, and variations that use $60 and $39c and $50 and $39c. Some of them even copying two sets at once.&lt;br /&gt;&lt;br /&gt;Running into the room with the guard in level 1 using the emulator, breaking and looking at all those areas allowed me to identify them more precisely. The 14th byte in each set is "CharID", an identifier for the character. It's 0 for the kid, 1 for the shadow, 2 for guards and so on. &lt;br /&gt;&lt;br /&gt;So CharData was the temporary working area at $40. KidData seemed to be at $50, since $5d always contained $00. At $6D I found $02 while the guard was on screen, so that must be ShadData. Then I assumed that the temporary area for storing the opponent, OppData must be $39c.&lt;br /&gt;So I looked at the various transfer functions and gave them appropriate names:&lt;br /&gt;&lt;br /&gt;D05A:loadKid&lt;br /&gt;D05D:loadShad&lt;br /&gt;D060:saveKid&lt;br /&gt;D063:saveShad&lt;br /&gt;D0A8:loadKidAndShad&lt;br /&gt;D0AB:saveKidAndShad&lt;br /&gt;D0B1:loadShadAndKid&lt;br /&gt;D0B4:saveShadAndKid&lt;br /&gt;&lt;br /&gt;I noticed that many of the routines were called via jump tables. So while $d05a is called when the game wants to load KidData into CharData, the actual function is at $d835.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;ld05a:&lt;br /&gt;            jmp ld835&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the disassembly, I've labelled the actual implementations with the ending "Impl":&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;loadKid:&lt;br /&gt;            jmp loadKidImpl&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The reason for this might have to do with the way Jordan's code was split into multiple object files and that he used these tables for linking. In any case, it was something I was able to optimize away. In the C64 version, loadKid is actually loadKidImpl. No jump tables.&lt;br /&gt;&lt;br /&gt;The two bytes CharSeqLo and CharSeqHi in CharData are especially interesting. They point to the location in the sequence table where the next byte is read from. To switch animations, the game must change this location to point to a different sequence. Thus the control code is very likely tied to the sequence switching. Easy enough, I found it immediately:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;setSequenceImpl:&lt;br /&gt;            sec&lt;br /&gt;            sbc #$01&lt;br /&gt;            asl&lt;br /&gt;            tax&lt;br /&gt;            lda SeqTableLo,x&lt;br /&gt;            sta CharSeqLo&lt;br /&gt;            lda SeqTableHi,x&lt;br /&gt;            sta CharSeqHi&lt;br /&gt;            rts&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The input in the accumulator is a "sequence id", subtracted by one and multiplied by two, it's used as the index into the beginning of the SeqTable data at $3000. Thus one of the pointers at the beginning is read and stored into CharSeqLo and CharSeqHi.&lt;br /&gt;So I noticed that it will be important to find and understand all the points where setSequence is called. Oh, and it is called a lot!&lt;br /&gt;&lt;br /&gt;For example like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;triggerStartRun:&lt;br /&gt;            lda #$01&lt;br /&gt;            jmp setSequence&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In lots of other cases also like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;triggerFallLand:&lt;br /&gt;            lda #$2e&lt;br /&gt;            jsr setSequence&lt;br /&gt;            jsr animChar&lt;br /&gt;            [...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The sequence is selected and then animChar is called. This routine is the sequence table parser I've explained in my previous post. So basically code like this sets the sequence and then immediately processes its first frame to make sure the CharData is up-to-date. Otherwise animChar is called only during the next normal frame update.&lt;br /&gt;&lt;br /&gt;The find out what each sequence looks like, and therefore to be able to identify the right sequence triggering code, I made a little test program that allowed me to trigger each sequence and play it. I came up with a long list of sequence ids:&lt;br /&gt;&lt;br /&gt;$01: STARTRUN&lt;br /&gt;$02: STAND&lt;br /&gt;$03: STANDING_JUMP&lt;br /&gt;$04: RUNNING_JUMP&lt;br /&gt;$05: TURN&lt;br /&gt;$06: RUNNING_TURN&lt;br /&gt;$07: FALL&lt;br /&gt;[...]&lt;br /&gt;&lt;br /&gt;and so on...&lt;br /&gt;&lt;br /&gt;This made it possible to find functions like triggerStartRun above.&lt;br /&gt;&lt;br /&gt;I then decided to roll up the code from the other end. Find the joystick code and see what it does. This was easy. Joysticks on the Apple II are analog and are read through the analog input registers at $c064. The code required for this is unusually complex (at least compared to the C64) but in the end I traced it to a handful of memory locations which I dubbed:&lt;br /&gt;&lt;br /&gt;0018:InputXDirection&lt;br /&gt;0019:InputYDirection&lt;br /&gt;001A:InputButton0&lt;br /&gt;001B:InputButton1&lt;br /&gt;&lt;br /&gt;The latter two are or-ed together and stored in:&lt;br /&gt;&lt;br /&gt;003D:InputButton&lt;br /&gt;&lt;br /&gt;Quick checks in the emulator verified my assumptions. &lt;br /&gt;Searching for these labels in the code now gave me lots of results. Many routines where reading these values and triggering various sequences. What I needed was the common root function that calls all of them. If I could find that, then I would be very close to the main update loop of the game.&lt;br /&gt;&lt;br /&gt;At this point my disassembly and labeling efforts became a bit fuzzy. I had to identify too many functions at once, and I wasn't quite sure of what many of them did in detail. But I traced my way up the call stack and found a couple of crucial ones:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;;----------------------------------&lt;br /&gt;updateCharacter:&lt;br /&gt;            lda CharFace&lt;br /&gt;            bpl updateCharacterMirrored&lt;br /&gt;            jmp updateCharacterNormal&lt;br /&gt;;----------------------------------&lt;br /&gt;updateCharacterMirrored:&lt;br /&gt;            jsr swapLeftRight&lt;br /&gt;            jsr updateCharacterNormal&lt;br /&gt;            jmp swapLeftRight&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After a while I found a function which was basically just a long list of JSR calls. It called loadKidAndShad at the beginning, it called animChar, then it read from the frame def list using CharFrame, and at the end it called saveKid. Many unknown calls in between.&lt;br /&gt;&lt;br /&gt;At this point everything went really quickly. I took that function and called it:&lt;br /&gt;&lt;br /&gt;22F4:update&lt;br /&gt;&lt;br /&gt;I decided that this must be as close to the main loop as I can get with my limited understanding of the code. I started to rebuild the known parts of this function in my own code, with many of the unknown JSR calls commented out. I just wanted to get the basic loop working: read from input -&amp;gt; run animation code -&amp;gt; get frame -&amp;gt; update sprite.&lt;br /&gt;I had the backend already, so I was making very quick progress.&lt;br /&gt;&lt;br /&gt;From the document I also knew how the level data is laid out. It's called "Level Blueprint" and it's one of the few data structures which is not only fully described in the document (see page 12 in the PDF), but also has it's memory location listed: &lt;br /&gt;&lt;br /&gt;B700:BlueType&lt;br /&gt;B9D0:BlueSpec&lt;br /&gt;BCA0:LinkLoc&lt;br /&gt;BDA0:LinkMap&lt;br /&gt;BEA0:Map&lt;br /&gt;BF00:Info&lt;br /&gt;&lt;br /&gt;I had already moved that chunk of memory into my own code. I found many routines dealing with these memory locations. I noticed that $24/$25 always points into the BlueType array, and $26/$27 points to BlueSpec:&lt;br /&gt;&lt;br /&gt;0024:CurrentBlueTypePtrLo&lt;br /&gt;0025:CurrentBlueTypePtrHi&lt;br /&gt;0026:CurrentBlueSpecPtrLo&lt;br /&gt;0027:CurrentBlueSpecPtrHi&lt;br /&gt;&lt;br /&gt;I was looking at a function called from within "update", which reads parts of the level data into smaller buffers. It turned out to be the collision detection code.&lt;br /&gt;It was easy enough to understand, I went through it and documented it almost completely in one go.&lt;br /&gt;&lt;br /&gt;4500:collisionDetection&lt;br /&gt;4503:applyCollisionDetectionResult&lt;br /&gt;4506:isPathBlocked&lt;br /&gt;&lt;br /&gt;I was on a roll. Within the next few days, I went through tons of code and the puzzle seemed to come together. I still had no idea what most of the code did, but the parts that I did understand were growing at a steady rate. It was hard to stop, since it was always possible to go on.&lt;br /&gt;At one point I did force myself to put all of the known code into my C64 program and see if I could get it up and running.&lt;br /&gt;&lt;br /&gt;I also took a screenshot of the DOS version and converted it to C64 specs.&amp;nbsp;I decided that I need to have something in the background of my animation test.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-mguspkGuNnw/Tp3nyWf5_WI/AAAAAAAAAEs/M3RupaBGVUI/s1600/testimage2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-mguspkGuNnw/Tp3nyWf5_WI/AAAAAAAAAEs/M3RupaBGVUI/s1600/testimage2.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;My backdrop, just a static image, but it gives context to the animations&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;The program I was putting together had many jumps to unknown code. I commented out as much as possible, figuring it wouldn't be important for now. I could always figure those out later. I knew that I had all the input handling, the animation update and the drawing code in place. Many functions that triggered the right sequences. And even collision detection, at least partially. I was confident that it could work.&lt;br /&gt;&lt;br /&gt;However it took my quite a few days and I had to fix many little problems along the way. Finally, on the 17th of July 2009, I managed to get it all working. I made this video:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/BoSMOOcLzTk/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/BoSMOOcLzTk?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/BoSMOOcLzTk?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;You can download a C64 .prg of the &lt;a href="http://twinbirds.com/prince_of_persia/pop_170709.prg"&gt;work in progress version from 17-Jul-2009&lt;/a&gt;. It contains this single playable room. It's not possible to progress to the next screen.&lt;br /&gt;&lt;br /&gt;I was very happy to have gotten this far. I had the Kid, the Prince of Persia, running and jumping on my screen. I was able to control it and perform all the normal actions. And it felt right. Timing, speed, animations. Of course it was spot on, it was using the original code written by Jordan Mechner, lifted from its Apple II grave and brought back to life, with a new purpose.&lt;br /&gt;&lt;br /&gt;At this point I was sure I could do this. It would only be a matter of months. Oh boy, was I wrong.&lt;br /&gt;&lt;br /&gt;In the next part, I'll talk about how I dived into the code that draws the screen and how I learned that 64KB of RAM is probably not going to cut it.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-1626432740043200208?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/1626432740043200208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/10/part-three-making-kid-come-to-life.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/1626432740043200208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/1626432740043200208'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/10/part-three-making-kid-come-to-life.html' title='Part Three - Making the Kid come to life'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-mguspkGuNnw/Tp3nyWf5_WI/AAAAAAAAAEs/M3RupaBGVUI/s72-c/testimage2.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-3281209184888633296</id><published>2011-10-17T14:49:00.000-07:00</published><updated>2011-10-17T14:56:05.891-07:00</updated><title type='text'>Part Two - In the beginning there was a binary data blob</title><content type='html'>If you haven't guessed yet, it's going to become more technical. You've been warned.&lt;br /&gt;&lt;br /&gt;So there I was on June 19th 2009. A printed copy of Jordan Mechner's &lt;a href="http://jordanmechner.com/wp-content/uploads/1989/10/popsource009.pdf"&gt;PoP source code documentation&lt;/a&gt;&amp;nbsp;in one hand, and &lt;a href="http://www.virtualii.com/"&gt;Virtual II&lt;/a&gt; (an excellent Apple II emulator for Mac OS X) running the original Prince of Persia game.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-MWwjQvJNWHg/Tpx7ouN8kXI/AAAAAAAAAD4/ckYodrG-U4o/s1600/virtualii.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="274" src="http://4.bp.blogspot.com/-MWwjQvJNWHg/Tpx7ouN8kXI/AAAAAAAAAD4/ckYodrG-U4o/s640/virtualii.png" width="640" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Running the Apple II version of Prince of Persia in the Virtual II emulator&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;I managed to save a snapshot of the game state at the very beginning of level 1 into a .vii file, and using the memory inspector in Virtual II and a hex editor in tandem I was able to isolate the two 64KB banks of memory in the snapshot file. I saved them as two binary files bank1_0000.bin and bank2_0000.bin.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-NPwfQqhcio0/Tpx_eKpZaHI/AAAAAAAAAEI/CvpZF4D8n5M/s1600/binary_blob.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-NPwfQqhcio0/Tpx_eKpZaHI/AAAAAAAAAEI/CvpZF4D8n5M/s1600/binary_blob.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Isn't it a pretty blob?&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;Then I took my favorite 6502 disassembler (&lt;a href="http://www.floodgap.com/retrotech/xa/"&gt;dxa&lt;/a&gt;) and let it process the data. I somehow ran it on bank 2 first, and that was a lucky coincidence, as it turned out that bank 2 contained most of the game's code. So let's just focus on that bank for now. To Apple II programmers, it is known as "Auxiliary Memory".&lt;br /&gt;The unlabeled output of the disassembler &lt;a href="http://twinbirds.com/prince_of_persia/disasm_bank2_unlabeled.txt"&gt;can be found here&lt;/a&gt;. It's all just unknown code with generic labels. Disassemblers try to identify data regions within the code, but typically they only have limited success. I've already correctly assigned the data regions in this output file, but originally it looked much worse.&lt;br /&gt;&lt;br /&gt;Here's the disassembled code of the hex editor window above, starting from $2000:&lt;br /&gt;&lt;br /&gt;&lt;pre style="white-space: pre-wrap; word-wrap: break-word;"&gt;;----------------------------------&lt;br /&gt;l2000:&lt;br /&gt;            jmp l2015&lt;br /&gt;;----------------------------------&lt;br /&gt;l2003:&lt;br /&gt;            jmp l20e2&lt;br /&gt;;----------------------------------&lt;br /&gt;l2006:&lt;br /&gt;            jmp l201e&lt;br /&gt;;----------------------------------&lt;br /&gt;l2009:&lt;br /&gt;            jmp l2029&lt;br /&gt;;----------------------------------&lt;br /&gt;l200c:&lt;br /&gt;            jmp lfc00&lt;br /&gt;;----------------------------------&lt;br /&gt;l200f:&lt;br /&gt;            jmp lfc00&lt;br /&gt;;----------------------------------&lt;br /&gt;l2012:&lt;br /&gt;            jmp l25aa&lt;br /&gt;;----------------------------------&lt;br /&gt;l2015:&lt;br /&gt;            sta lc009&lt;br /&gt;            jsr l2046&lt;br /&gt;            jmp l20e2&lt;br /&gt;;----------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, this makes no sense. Yes, it's a jump table of sorts, but what is the purpose of these subroutines. What do they do? How can we find out?&lt;br /&gt;&lt;br /&gt;One approach is to identify memory-mapped IO registers (soft switches in Apple II lingo). There's one right there: $c009. It's used to switch the zero page and stack areas of the 6502 ($0000-$01ff) to auxiliary memory.&lt;br /&gt;Looking at all the code that's using soft switches allows you to find the parts where joysticks, keyboard and other hardware is accessed. This is one way of entering the terra incognito of the game's code.&lt;br /&gt;&lt;br /&gt;However, I used a different approach. I started with the immediate goal of finding the data describing the animation frames, because that's what I needed to improve my existing program, which was still based on FreePrince.&lt;br /&gt;So I consulted the source code documentation to see what it has to say about animation data.&lt;br /&gt;&lt;br /&gt;One of the most interesting parts I noticed were the two pages titled "Character animation comments" (starting at page 17 of the PDF). They describe 16 bytes of data holding most of the character's state, its position, velocity, health, a pointer into the "sequence table" and its current entry in the "frame list". It mentioned frame #15 as being standing still. Guess what, the 15th frame extracted from the DOS version is the Prince "standing still". I was onto something.&lt;br /&gt;&lt;br /&gt;In a different part of the document (on page 15) the sequence table is mentioned again, together with the "frame def list". It was clearly obvious that the frame def list contained information about each of the images used to animate the character. Index into image table, x and y shift, and more.&lt;br /&gt;It seemed like the&amp;nbsp;sequence table was a list of frames with embedded commands. Positive values are frames, negative values are commands. Simple. The commands were listed right there: goto, aboutface, up, down, etc.&lt;br /&gt;It was clear to me that if I would find the frame def list or the sequence table in memory, I could use the Virtual II debugger to find the code accessing it, and then I would be right in the heart of the animation code. But where could they be?&lt;br /&gt;&lt;br /&gt;It felt like searching for a needle in a haystack. Surely they can't be that small (frame def list is described to be 1200 bytes in size), but I just couldn't pinpoint them with enough confidence. I thought if the PC version is based on the same animation code, then maybe the same sequence table is used there. I looked at the data files that come with the Prince of Persia for DOS and only found level data. The data file format had been reverse engineered and was clearly documented. The files mostly just contained bitmap data, wav files, midi files and palettes. No sign of a frame list or animation sequence table.&lt;br /&gt;Then it struck me. The data must be in the PRINCE.EXE file itself, not in an external data file. But that meant it was probably just as hard to find. Except if it really was similar to the Apple II table. From reading Jordan's journals it was clear to me that Lance Groody (who programmed the DOS version) was working off of Jordan's code and data. Why would he change the animation sequence table? He would have had no reason to do that.&lt;br /&gt;So in a desperate attempt, I started my copy of Araxis Merge in binary comparison mode, dragged in bank2_0000.bin onto the left pane and PRINCE.EXE onto the right pane and was hoping for a comparison match. Nothing. Too many differences, no direct matches. I removed everything that looked like code and text from the .EXE file to make it easier for Araxis to find a match. Still nothing. I scrolled aimlessly through the files and then I saw it:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-UAHT22u5fgA/TpyRwNS8yvI/AAAAAAAAAEY/gxjPB19206E/s1600/foundit.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-UAHT22u5fgA/TpyRwNS8yvI/AAAAAAAAAEY/gxjPB19206E/s1600/foundit.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Similar data in the Apple II memory (left) and in the PC executable (right). Found it!&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;I traced the data pattern to its beginning in the Apple II memory dump, and saw that it starts at $2800. It seemed to be larger than 1200 bytes though. I scrolled on to find its end.&lt;br /&gt;The memory layout clearly changed at the $3000 point, so that must be it. At that location I noticed something that looked like a table of addresses:&lt;br /&gt;&lt;br /&gt;&lt;pre style="white-space: pre-wrap; word-wrap: break-word;"&gt;E9 30 16 31 09 32 43 32 AE 32 CF 32 50 33 C9 33 16 34 79 34 9A 34 C0 34 C6 34 DF 34 6D 35 F9 33 A2 37 91 33 76 33 D5 37 AD 33 17 38 AD 34 DE 33 46 34 7A 32 90 32 F4 34 17 37 0B 37 FB 36 EB 36 D6 36 C1 36 AC 36 92 36 8C 36 70 36 53 36 34 36 15 36 F6 35 C8 32 D2 35 7F 35 B4 35 75 35 12 35 2C 37 20 37 39 38 4C 38 4F 38 44 38 3A 31 89 31 A4 31 B2 31 F2 31 F9 31 D9 31 D4 31 DE 31 E4 31 EB 31 D0 31 6F 31 63 34 CA 31 58 38 29 38 F1 35 53 34 52 31 B8 31 7D 31 1E 31 82 37 90 32 01 32 FA 32 16 33 34 33 E4 30 24 38 98 31 1C 31 22 31 A2 32 37 31 43 37 53 37 74 37 14 39 B5 38 CE 38 E5 38 18 39 27 39 EC 38 ED 39 B9 38 3B 39 6F 33 C4 39 D2 39 DA 39 43 39 3F 39 65 39 69 39 7B 39 BF 39 D6 39&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In little-endian format, the addresses $30e9, $3116, $3209, $3243, and so on. They ended at $30e4, suspiciously close to the first entry of $30e9. So a pointer table into a data structure.&lt;br /&gt;I checked the data of the first pointer:&lt;br /&gt;&lt;br /&gt;&lt;pre style="white-space: pre-wrap; word-wrap: break-word;"&gt;$30e9 : F9 01 01 02 03 04 FB 08 05 FB 03 06 FB 03 07 FB 05 08 FB 01 F2 01 09 FB 02&lt;/pre&gt;&lt;br /&gt;Negative number, some positive numbers, then a negative number, and so on. Could it be? Did I just look at the sequence table?&lt;br /&gt;&lt;br /&gt;What if the sequence table instructions were listed starting at $FF?&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-7M7qaZyFBA4/TpyV-jFBFQI/AAAAAAAAAEg/cJAtAok-Cdg/s1600/seq_commands.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-7M7qaZyFBA4/TpyV-jFBFQI/AAAAAAAAAEg/cJAtAok-Cdg/s1600/seq_commands.jpg" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;My annotations of the sequence instructions in Jordan's document&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;So lets look at the sequence above. Given my assumption it would translate into:&lt;br /&gt;&lt;br /&gt;&lt;pre style="white-space: pre-wrap; word-wrap: break-word;"&gt;F9 01    act 01 ; change action code to "running, jumping, ..."&lt;br /&gt;01              ; show frame 1&lt;br /&gt;02              ; show frame 2&lt;br /&gt;03              ; show frame 3&lt;br /&gt;04              ; show frame 4&lt;br /&gt;FB 08    chx 08 ; KIDX := KIDX + 8&lt;br /&gt;05              ; show frame 5&lt;br /&gt;FB 03    chx 08 ; KIDX := KIDX + 3&lt;br /&gt;06              ; show frame 6&lt;br /&gt;FB 03    chx 08 ; KIDX := KIDX + 3&lt;br /&gt;07              ; show frame 7&lt;br /&gt;FB 05    chx 08 ; KIDX := KIDX + 5&lt;br /&gt;08              ; show frame 8&lt;br /&gt;FB 01    chx 08 ; KIDX := KIDX + 1&lt;br /&gt;F2 01           ; was unknown at first, turned out the play the footstep sound&lt;br /&gt;09              ; show frame 9&lt;br /&gt;FB 02    chx 08 ; KIDX := KIDX + 2&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;and so on...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It made perfect sense. The first few frames in my list of images were part of the running animation. I was looking at the sequence for making the kid run. Although it seemed there were more than the 8 documented instructions used in the sequence table. I had to find out what the others did.&lt;/div&gt;&lt;div&gt;Now I was on fire. I quickly debugged through the code and set strategic memory watch points to find the code using these two tables. I found the sequence table parser first:&lt;/div&gt;&lt;br /&gt;&lt;pre style="white-space: pre-wrap; word-wrap: break-word;"&gt;;----------------------------------&lt;br /&gt;l4945:&lt;br /&gt;            jsr ld003   ; read a byte from the seq table&lt;br /&gt;            cmp #$fb    ; is it CHX?&lt;br /&gt;            bne l4957   ; no, then go to l4957&lt;br /&gt;l494c:&lt;br /&gt;            jsr ld003   ; read parameter byte of CHX instruction&lt;br /&gt;            jsr ld015   ; perform CHX instruction&lt;br /&gt;            sta l41&lt;br /&gt;            jmp l4945   ; loop back to read next byte&lt;br /&gt;;----------------------------------&lt;br /&gt;l4957:&lt;br /&gt;            cmp #$fa    ; is it CHY?&lt;br /&gt;            bne l4966   ; no, then go to l4966&lt;br /&gt;l495b:&lt;br /&gt;            jsr ld003   ; read parameter byte of CHY instruction&lt;br /&gt;            clc         ; perform CHY instruction&lt;br /&gt;            adc l42&lt;br /&gt;            sta l42&lt;br /&gt;            jmp l4945   ; loop back to read next byte&lt;br /&gt;;----------------------------------&lt;br /&gt;l4966:&lt;br /&gt;            cmp #$fe    ; is it ABOUTFACE?&lt;br /&gt;            bne l4973   ; no, then go to l4973&lt;br /&gt;l496a:&lt;br /&gt;            lda l43     ; perform ABOUTFACE instruction&lt;br /&gt;            eor #$ff&lt;br /&gt;            sta l43&lt;br /&gt;            jmp l4945   ; loop back to read next byte&lt;br /&gt;&lt;/pre&gt;&lt;pre style="white-space: pre-wrap; word-wrap: break-word;"&gt;[and so on for all other commands]&lt;/pre&gt;&lt;br /&gt;I had the animation system by the balls. Can you guess which part I found next, just by looking at this parsing code? I'll tell you in the next part.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-3281209184888633296?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/3281209184888633296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/10/part-two-in-beginning-there-was-binary.html#comment-form' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/3281209184888633296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/3281209184888633296'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/10/part-two-in-beginning-there-was-binary.html' title='Part Two - In the beginning there was a binary data blob'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-MWwjQvJNWHg/Tpx7ouN8kXI/AAAAAAAAAD4/ckYodrG-U4o/s72-c/virtualii.png' height='72' width='72'/><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-2066006457219367658</id><published>2011-10-17T06:53:00.000-07:00</published><updated>2011-10-17T10:31:07.488-07:00</updated><title type='text'>Part One - Why the hell would anyone want to do that?</title><content type='html'>So how and when did I start to work on a C64 version of Prince of Persia? And for the love of god, why?&lt;br /&gt;&lt;br /&gt;In October of 2008 I visited the &lt;a href="http://twitter.com/#%21/xparty"&gt;X party&lt;/a&gt;, a Commodore 64 demo scene event in the Netherlands. This sparked my interest in actively doing C64 programming again, something I haven't really done in the 15 years before that, even though I still kept up with the developments in that community.&lt;br /&gt;At this point I actively started looking for a C64 project, and a game conversion seemed like a good idea, given my background as a game developer. I was rusty and needed something bordering on the trivial to get my 6502 assembly juices flowing again.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://noname.c64.org/csdb/gfx/releases/4000/4527.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://noname.c64.org/csdb/gfx/releases/4000/4527.gif" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Prince of Persia preview by Poharai Attila and Varga Viktor from Hungary&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;So in May of 2009 I found the &lt;a href="http://noname.c64.org/csdb/release/?id=4527"&gt;Prince of Persia preview on CSDb&lt;/a&gt;.&amp;nbsp;It's an attempt&amp;nbsp;from the 1990s&amp;nbsp;at converting Prince of Persia, that unfortunately only produced a slide show of some of the level graphics. But the comment beneath, by Mikael "Twoflower" Backlund got my attention: "A C64 version of this game would sure be something.". I immediately had the same feeling. And then he wrote: "Furthermore, the sourcecode to one of the 8-bit versions is out there.".&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;That made me all warm and fuzzy inside. But soon I had to find out that what he meant was the version of Prince of Persia for the &lt;a href="http://en.wikipedia.org/wiki/SAM_Coup%C3%A9"&gt;SAM Coupe&lt;/a&gt; computer. Although an outstanding technical achievement, it was not a direct port of the original game, but rather a fan-made recreation, which was close to Jordan Mechner's classic, but also different in lots of ways. And it was for a Z80-based architecture, which I personally never worked on and have little passion for.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://www.princed.org/content/files/screenshots/freeprince6.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="240" src="http://www.princed.org/content/files/screenshots/freeprince6.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;FreePrince project&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;But the idea stuck with me. Is there someone who has done the reverse-engineering work of Prince of Persia? Something that I could just take and port to the C64 quickly?&lt;br /&gt;I kept digging and immediately came across the &lt;a href="http://www.princed.org/freeprincejsp/"&gt;FreePrince project&lt;/a&gt;. This is your typical open-source version of a classic game, created by a group of enthusiasts, using the trusted SDL framework. I thought that I had struck gold. Although it seemed unmaintained and it was hard to find the source code, it was a somewhat working version of Prince of Persia using the original game's graphics and level data. It had a complex animation state machine and lots of things working, so I assumed that it was based on disassembly of the PC or Amiga version. Plus it pointed me towards the &lt;a href="http://www.princed.org/"&gt;Princed project&lt;/a&gt;&amp;nbsp;and their tool "PR", a Prince of Persia resource extraction tool.&lt;br /&gt;The latter can be used to unpack the data files that came with the DOS version of Prince of Persia to get the individual images, sounds and palettes that make up the assets of the game.&lt;br /&gt;So, without further ado, I started hacking away. I took the 216 animation frames of the Prince extracted by PR, and first converted them to C64 resolution and colors.&lt;br /&gt;&lt;br /&gt;The C64 features a multi-color mode that is using two bits to represent one pixel. This means you can use four colors, which you can select from the sixteen available colors. Since the memory storage size for graphics is the same in single-color and in multi-color mode, the graphics chip has to halve the horizontal resolution of 320 pixels in order to use two bits per pixel instead of one. So the effective resolution for multi-color graphics is 160x200, which is also half of the resolution of the DOS game. And since one of the 4 colors has to be the transparent color that meant that only 3 colors are available for the Prince character. I quickly settled on white for the clothes, light red for the skin and brown or orange for the hair. Using semi-automatic conversion tools I came up with this:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ZmRJAmfEuO0/TpvUTS82VgI/AAAAAAAAADI/Xe7isjUf1dA/s1600/res00415.bmp" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-ZmRJAmfEuO0/TpvUTS82VgI/AAAAAAAAADI/Xe7isjUf1dA/s200/res00415.bmp" width="58" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Original image from DOS version&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-IfZkkHl-rT8/TpvUcJPlMfI/AAAAAAAAADQ/VVQ7MI5oXOE/s1600/res00415.png" imageanchor="1" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-IfZkkHl-rT8/TpvUcJPlMfI/AAAAAAAAADQ/VVQ7MI5oXOE/s200/res00415.png" width="58" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Automatic conversion to C64 resolution and colors&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Yes, that's a lot of detail lost, but it was inline with other C64 games. I was sure I'd be able to find some graphics artist who could polish that up later.&lt;br /&gt;Doing this conversion for all the 216 animation frames quickly gave me a complete set to be used on the C64.&lt;br /&gt;So at this point I fired up my source code editor. I took at look at the source of Freeprince, and started to implement its animation state machine in assembly code. I laid out the Prince animation frames as sprites in memory and immediately noticed that it's gonna be tight. Those animations sure need a lot of memory. But that would be for later to worry about.&lt;br /&gt;Within a couple of days I had a simple working version. Basic animations states were implemented, such as running, jumping, crouching, etc. On June 15th 2009 I captured a video and showed it to one or two people.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/AYpNhomTIxM/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/AYpNhomTIxM?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/AYpNhomTIxM?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;Download a C64 .prg file of the &lt;a href="http://twinbirds.com/prince_of_persia/pop_150609.prg"&gt;work in progress version from 15-Jun-2009&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The response was a bit muted. Not enough of the game there yet. Also the animations looked dodgy. The positioning of the frames used in FreePrince was not based on the original game. Someone must've redone that manually, and so the locations were wrong.&lt;br /&gt;So I kinda went off in the wrong direction. FreePrince certainly wouldn't be good enough for a faithful conversion. It was also incomplete and not fully playable.&lt;br /&gt;&lt;br /&gt;Back to square one. I had to look for better source material. Disassemble the DOS version?&lt;br /&gt;In the meantime I found &lt;a href="http://jordanmechner.com/"&gt;Jordan Mechner's blog&lt;/a&gt;. He had the courage and insight to post all of his old journals from the 1980s. He meticulously kept a log of his daily work. What a great read that was. Just a few days before I started looking for Prince of Persia information Jordan also posted &lt;a href="http://jordanmechner.com/blog/2009/05/prince-of-persia-released/"&gt;this article&lt;/a&gt; on his blog. It contained a link to a PDF, which turned out to be the &lt;a href="http://jordanmechner.com/wp-content/uploads/1989/10/popsource009.pdf"&gt;Prince of Persia source code documentation&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I was amazed. The source was lost on Apple II disks, but the document written just a few days after the release in 1989 was there, with all kinds of juicy little details about the graphics engine, the data structures, lists of images, and more. It was like someone had handed me the key to a long lost treasure. I didn't even know that there was an Apple II version of the game, and that it was the original version. I learned that all others were ports (or ports of ports) of that one original. And it was all in perfect 6502 code, which I could read and which would run fine on the 6510 (a 6502 variant) of the Commodore 64. Sure it said it would need 128KB of RAM, but at that time that seemed like a small challenge. I imagined it to be very inefficient code and easy to optimize. Piece of cake, right?&lt;br /&gt;&lt;br /&gt;So how do you go about reverse-engineering 128K of code and data? It's all just a big pile of binary data. It's like one really big crossword puzzle.Where do you start?&lt;br /&gt;I'll cover that in my next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-2066006457219367658?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/2066006457219367658/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/10/part-one-why-hell-would-anyone-want-to.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/2066006457219367658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/2066006457219367658'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/10/part-one-why-hell-would-anyone-want-to.html' title='Part One - Why the hell would anyone want to do that?'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-ZmRJAmfEuO0/TpvUTS82VgI/AAAAAAAAADI/Xe7isjUf1dA/s72-c/res00415.bmp' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1155250278529986220.post-1717640841496087722</id><published>2011-10-16T00:19:00.000-07:00</published><updated>2011-12-13T02:47:15.276-08:00</updated><title type='text'>Prince of Persia for Commodore 64/128 released</title><content type='html'>The C64 conversion of Prince of Persia, based on the original Apple II code by Jordan Mechner has been released today.&lt;br /&gt;It requires a Commodore 64 or 128 (or maybe even a C64GS if you own one) and an EasyFlash (or 100% compatible) cartridge.&lt;br /&gt;You can download the cartridge image from &lt;a href="http://noname.c64.org/csdb/release/?id=102540"&gt;this CSDb page&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Here are some screenshots and a few movies of the game:&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/title.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/title.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/story1.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/story1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/story2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/story2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/cutscene.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/cutscene.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/level1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/level1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/level2.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/level2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/level3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/level3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/level4.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/level4.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/level5.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/level5.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://twinbirds.com/prince_of_persia/dead.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://twinbirds.com/prince_of_persia/dead.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/C8DDd7gEnkc/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/C8DDd7gEnkc?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/C8DDd7gEnkc?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/tBs5-WOtIpc/0.jpg" height="290" width="384"&gt;&lt;param name="movie" value="http://www.youtube.com/v/tBs5-WOtIpc?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="384" height="290"  src="http://www.youtube.com/v/tBs5-WOtIpc?version=3&amp;f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;During the next couple of weeks I will post my development diary. Details about the work on this conversion, pitfalls and little success stories. In short, pure C64 coder drama. Don't miss it!&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1155250278529986220-1717640841496087722?l=popc64.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://popc64.blogspot.com/feeds/1717640841496087722/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://popc64.blogspot.com/2011/10/prince-of-persia-for-commodore-64128.html#comment-form' title='39 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/1717640841496087722'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1155250278529986220/posts/default/1717640841496087722'/><link rel='alternate' type='text/html' href='http://popc64.blogspot.com/2011/10/prince-of-persia-for-commodore-64128.html' title='Prince of Persia for Commodore 64/128 released'/><author><name>mrsid</name><uri>http://www.blogger.com/profile/11145154244292497777</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-279mq9zmBl8/TpsscoDFEwI/AAAAAAAAACc/hGyk598WY6w/s220/081f6a6.jpg'/></author><thr:total>39</thr:total></entry></feed>
