6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 24, 2024 4:03 am

All times are UTC




Post new topic Reply to topic  [ 9 posts ] 
Author Message
PostPosted: Thu Jun 06, 2013 9:31 am 
Offline

Joined: Wed May 20, 2009 1:06 pm
Posts: 491
How I wrote Pitfall for the Atari 2600

Quote:
Famed game designer David Crane talks about his design challenges creating Pitfall for the Atari 2600.


http://www.youtube.com/watch?v=MBT1OK6VAIU

Via:

http://hackaday.com/2013/06/05/retrotec ... tari-2600/


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 06, 2013 8:18 pm 
Offline
User avatar

Joined: Mon Aug 08, 2011 2:48 pm
Posts: 808
Location: Croatia
Interesting video, but what cough my interest was the segment in which he mentioned self modifying code. Is there a good example of such a code for the 6502 where modifying a function works better than using a solid function?


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 06, 2013 9:11 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10986
Location: England
If you put code in zero page - which happens to be the only RAM in that platform - then your self-modifications can use zero page addressing, which is faster and smaller. If your code needs to read or write indirectly, you can use absolute addressing and modify the operands instead of taking the indirection penalty: the operands of absolute instructions become the zero-page pointers.


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 06, 2013 10:07 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8514
Location: Midwestern USA
BigEd wrote:
If you put code in zero page - which happens to be the only RAM in that platform - then your self-modifications can use zero page addressing, which is faster and smaller. If your code needs to read or write indirectly, you can use absolute addressing and modify the operands instead of taking the indirection penalty: the operands of absolute instructions become the zero-page pointers.

The CHRGET subroutine in the Commodore 64's BASIC interpreter immediately comes to mind. CHRGET's job is to read a byte from BASIC program text and then perform some logical operations on it to determine its nature (token, comma, numeral, etc.). CHRGET runs on page zero and the text pointer is the 16 bit operand of an LDA instruction. If entered at CHRGET, the subroutine increments the pointer and then reads whatever it is pointing to. If entered at the CHRGOT alternate entry point, the pointer is not incremented, which means the previous byte is gotten again. As the text pointer is embedded in CHRGET, the code is self-modifying, and thus an exact example of what Ed is describing.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 07, 2013 2:14 am 
Offline

Joined: Sat Oct 20, 2012 8:41 pm
Posts: 87
Location: San Diego
Dajgoro wrote:
Interesting video, but what cough my interest was the segment in which he mentioned self modifying code. Is there a good example of such a code for the 6502 where modifying a function works better than using a solid function?


That reminds me of the Prodos low level disk driver code used by the Apple that had self modifying code. It was fairly small code that I wanted to put in a EPROM for my own use for simple disk reading operations. The code was all over the place by modifying the program itself to change pointers and even branch instructions, what a mess. I gave up on that idea. :roll:

It would have been faster code if it used zero page addressing for the pointers but I think 'maybe' they were trying to keep the driver safer from being modified. :?:


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 07, 2013 4:21 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 679
Inner loops with selfmodded indexed absolute addressing are usually faster than using indirect addressing, depending on how many iterations it does. Any time you can use a vector multiple times between changes you could benefit from self-modification.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 07, 2013 4:34 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8514
Location: Midwestern USA
clockpulse wrote:
That reminds me of the Prodos low level disk driver code used by the Apple that had self modifying code. It was fairly small code that I wanted to put in a EPROM for my own use for simple disk reading operations. The code was all over the place by modifying the program itself to change pointers and even branch instructions, what a mess. I gave up on that idea. :roll:

It would have been faster code if it used zero page addressing for the pointers but I think 'maybe' they were trying to keep the driver safer from being modified. :?:

It could be that obfuscation was a goal, but I suspect speed of execution was paramount.

However, self-modifying code may be the only way to get a task accomplished. Take, for instance, the 65C816's MVN and MVP block copy instructions. These instructions have only one addressing mode, which is a form of immediate mode, but without the octothorpe indicating so in the assembly language syntax. Two comma-separated operands are coded, the destination bank and source bank:


Code:
MVN SBNK,DBNK

or

Code:
MVP SBNK,DBNK

where SBNK is the source bank and DBNK is the destination. Since the banks are operands, how would you change them at run time? Obviously you'd have to use self-modifying code to do it.

Incidentally, older version of the UNIX kernel permitted self-modifying code to execute in user space. Once the notion of shared text regions took over the idea of allowing code to self-modify became a liability and as far as I know, self-modifying programs will cause a segfault or similar on all modern systems.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 13, 2013 1:58 am 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
I've oversimplified somewhat below, but this is the gist of it.

BDD is right in that top execution speed was a goal, but there is another consideration. Because of the way the 5.25" disk hardware works, when writing data (sync bytes are a slightly different beast) you must write to the (memory-mapped) I/O location exactly every 32 cycles, no more, no less; thus loops required precise timing, and addressing mode selection is paramount. And watch out for page boundary crossings! Reading doesn't require exact timing, but it has to be fast; if it takes longer than 32 cycles you'll miss bits.

When you read or write to the I/O location (assuming you read valid data), you get (when reading) or use (when writing) one of 64 possible values, called a disk nibble (in Apple II terminology). So you have 6 bits of information, and you need 4 disk nibbles to get 3 data (8-bit) bytes. (So each 256-byte sector has 342 data nibbles, with 4 bits being unused, i.e. 342 * 6 = 256 * 8 + 4). This (6-bit) disk nibble to (8-bit) data byte mapping is known as (again, in Apple II terminology) 6-and-2 encoding.

The DOS 3.3 disk driver (known as RWTS) was straightforward, and had no self-modified code (at least not that I can remember). To read a sector, read and store the disk nibbles in a (342-byte) RAM buffer in one pass, then make a second pass to convert disk nibbles to data bytes. To write a sector, it's the opposite order: convert data bytes to disk nibbles in one pass, then write the disk nibbles from the RAM buffer in the second pass, and this second pass requires exact timing but none of the other three passes do. (Formatting a disk also requires exact timing, since data is being written then as well.)

The ProDOS disk driver does a read & convert all in one pass (there is still some preparation before writing data, though). Why do this? Well, there are 16 (256-byte) sectors in each (circular) track: 0, 1, 2, 3, and so on. Since there is some processing after reading or writing, say sector 0, the disk will already be past sector 1, so DOS 3.3 and ProDOS use a 2:1 interleave, in other words, it reads and writes in the order: 0, 2, 4, ..., 12, 14, 1, 3, 5, and so on.

In DOS 3.3, RWTS itself is fast enough even with two passes to handle a 2:1 interleave (booting could read an entire track in 2 revolutions), but the File Manager (as its name implies, that's the part of DOS 3.3 that deals with things on a file level), was brutally inefficient, and after reading sector 0, with all of the subsequent processing, it had missed sector 2, and had to make a complete revolution to get back to sector 2. Thus a command like RUN APPLE-VISION took 18 (!) revolutions to read an entire track. (Yes, 18; remember, one revolution gets you from track 0 to track 0, it takes another 1/8 revolution to get to sector 2.)

DOS 3.3 could be (and was) patched to be somewhat smarter about things, and not miss sector 2, which sped things up considerably, but no doubt one of the intentions in ProDOS was to leave as much time as possible for file-level processing.


Top
 Profile  
Reply with quote  
PostPosted: Sun Jun 16, 2013 2:23 am 
Offline

Joined: Sat Oct 20, 2012 8:41 pm
Posts: 87
Location: San Diego
I did finally get it but it was really tough to disassemble the code because of jumps/branches to the middle of instructions on quite a few occasions. They also used an absolute LDA to a zero page address. A few things you might see when trying to disassemble a computer game of the 80's.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 46 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: