CBM64 Bard's Tale 1 Disassembly

Any developer realated stuff
User avatar
Darendor
Posts: 1502
Joined: Wed Jan 14, 2009 1:53 am
Location: Red Deer, Alberta, Canada

Post by Darendor »

ZeroZero wrote:Did you read the city map post??? ;)
Yes:
"street names in NM10"

Uh...:?
User avatar
Darendor
Posts: 1502
Joined: Wed Jan 14, 2009 1:53 am
Location: Red Deer, Alberta, Canada

Post by Darendor »

ZeroZero wrote:The teleport in sinister street...

... is located in file NM09, which handles the city.
It is at file-offset 5e7 (incl. 2 bytes for prg load address)
In memory it will be loaded at b3e5.

This assembler snippet shows, how the party's position
in town ($28 for north, $29 for east) will be tested for
N 2, E 25, and if it matches, positions the party at N 7, E 25.
That's the whole thing...

Code: Select all

b3e5:05e7:1   a5 28             lda $28
b3e7:05e9:1   c9 02             cmp #$02
b3e9:05eb:1   d0 0e             bne $b3f9
b3eb:05ed:1   a5 29             lda $29
b3ed:05ef:1   c9 19             cmp #$19
b3ef:05f1:1   d0 08             bne $b3f9
b3f1:05f3:1   a9 07             lda #$07
b3f3:05f5:1   85 28             sta $28
b3f5:05f7:1   a9 19             lda #$19
b3f7:05f9:1   85 29             sta $29
b3f9:05fb:1   60                rts 
Y'know, I actually understood that entire bit of ML coding. 8)
User avatar
Darendor
Posts: 1502
Joined: Wed Jan 14, 2009 1:53 am
Location: Red Deer, Alberta, Canada

Post by Darendor »

And about the BTI coding, I found most of the characters:

Code: Select all

Note: cf is used for both captial "O" and zero.

c0=?   c1=A   c2=B   c5=E   c6=F   c7=G   c8=H   cb=K cc=L   cf=0/0 cd=M   ce=N d0=P   d2=R   d3=S   d4=T   d9=Y   
00=[CR]a0=_   a1=!   a2="   a7='   a8=(   a9=)   ac=,   ad=-   ae=.   ba=:   bb=;   b1=1   b5=5
e1=a   e2=b   e3=c   e4=d   e5=e   e6=f   e7=g   e8=h   e9=i   ea=j   eb=k
ec=l   ed=m   ee=n   ef=o   f0=p   f2=r   f3=s   f4=t   f5=u   f6=v   f7=w   f8=x
f9=y   bf=?   db=[   dd=]   ff=>
                  
dc=[end]
User avatar
ZeroZero
Posts: 286
Joined: Tue Mar 10, 2009 9:10 pm
Location: Germany

Post by ZeroZero »

I'd love to start publishing disassembled events now, but some of the listings are rather long, and I didn't find out, how to post the code in an scrollable subwindow.
User avatar
Darendor
Posts: 1502
Joined: Wed Jan 14, 2009 1:53 am
Location: Red Deer, Alberta, Canada

Post by Darendor »

Why do you need to put it in a scrollable window?:?
User avatar
ZeroZero
Posts: 286
Joined: Tue Mar 10, 2009 9:10 pm
Location: Germany

Post by ZeroZero »

I T E M # 10

Last update: 2010-03-22

Graphics file format... (based on the post of tedious)

Graphic files are from NM50 to NM90, the digits are in hex.

I have finished a VB6 program that perfectly reads and displays
the gfx from a D64-image of the CHAR or DUNG disks. It fully
supports the animation. By that process I was able to learn (the
hard way) the last details, that I updated below.

On both disks, city and dungeon, is sufficient space, as well in directory
entries, as also in free blocks, to support 80+ images, and the images
do not need to be alike on the two disks, i.e. NM50 can be the guild on
city disk, but can be a monster on dungeon disk. Also, all unused
filenames in the range of NM00 to NMFF can be used to represent
graphics, so a lot more than the 65 in BT1 can be used.
However, the unmodified original BT1 game relies on certain files for
certain monster and building gfx, and I haven't yet found out where these
tables are located. Anyway modified BT1 games can be a lot richer in gfx.

Code: Select all

The graphics from the NM50 to NM90 files all have 
a size of 76 x 86 pixels, where on the CBM64 the 
pixels are double-width in multicolor mode. 

Due to the organization of the CBM64's hires memory 
the program uses several tables to create the correct 
offsets. 

At $c900 is a table of lo-bytes for a pointer into the 
hires memory to address where a certain graphic byte 
goes to. The hi-bytes are in a table at $ca00. 

For animation there are modifier tables that modify the 
address evaluated from $C900 and $ca00, they are located 
at $40c2 for lo-bytes and $4162 for the hi-bytes. 

If the position byte occurs, the next two bytes do not 
specify the x,y position of the frame, but the offsets 
into the above tables to evaluate the hires mem position 
at that that frame is to be drawn. 
However, to simply decode the pictures you can take the
next two bytes and substract 8 from each to get the
position in the picture. The first byte following is the
Y-Position or row, and the second byte is the X or column.

Now for the gfx data itself, this was nicely explained by 
tedious. 

The files contain: 
byte 0, 1   file load address 

One or more frames. First frame always is the main frame. 
After each frame there is another byte that gives the time 
that that frame will stay on screen before the next frame 
starts (in about 1/8th of seconds). 

The first frame ends upon the last byte decoded at
position y=85 and x=36. The next byte is its frame time
byte. If this is zero, there are no more frames.

Every next frame can end upon either of this two conditions:
a) a position tag is given with the X or column having
the value of $ff. The next byte then has the frame time,
if it were zero, the file is done and the byte after this
has the timecode.
b) while decoding you reach the last byte position at
row 85 and column 10 (10 bytes x 4 pixeld = 40!). The
next byte then has the time code for the animation. If
it were 0, it indicates end of file and the next byte
has the time code.

The main frame is called directly after loading the file with 
a jump into $4000. Animated frames are handled during the 
interrupt by calling a sub at $401f. 

For animation, each frame builds up on the complete former
frame, this means: you cannot directly put frame 3 on frame 1
and have the same result that you get, when you correctly
put frame 1, on that frame 2 and then on that frame 3.
Consequently, after the animation flips over, it starts
again with the main frame with the full picture data.

Every frame has this structure in the file: 

byte 0	tag for the runlength encoding. It is 
	always a byte, that does not occur in 
	the gfx data, because else it had to be 
	escaped during encoding. Since the encoding 
	requires 3 bytes, it only happens if more 
	than three same bytes follow in sequence. 
	The amount of equal bytes is the byte that 
	follows the tag + 3 more (for the minimum 
	of 3). The byte to multiply is the second 
	byte after the tag. 
byte 1	tag for position change. It is always a byte, 
	that does not occur in the gfx data, because 
	else it had to be escaped during encoding. 
	The two bytes following such a tag are the 
	offsets (see above) into the pointer tables. 
	If the second byte is $ff, that frame is 
	finished.
	For decoding, you can simply substract 8 from
	the next two bytes for the Y and X position.
byte 3	the data. 
	A data byte represent 4 pixels in a row, two 
	bits per pixel. Bit values mean: 
	00 = color 0, always black 
	01 = color 1, see below 
	10 = color 2, see below 
	11 = color 3, always white 
	If a pos tag occurs, the new byte offset into 
	hires mem is retrieved from the following two 
	bytes and drawing continues at the newly 
	calculated byte position. 
	If a run tag occurs, the next byte indicates 
	the number of repeated bytes, increased by the 
	minimum of 3. The byte after that holds the 
	data byte. 
	If another value is found, that value is a 
	data byte that only is used for one repeat. 
byte x	frame time in 1/8th of seconds. For the last 
	frame of an animation this contains 0 and 
	another byte is read for the frame time. 

The picture data is build vertically and interlaced, since the 
Apple II used interlaced graphics, and that is the system BT1 
was first made for. 
So graphic data will be build up like this: 
- every other line in first column for whole pic height 
  (0, 2, 4, ..., 84) 
- same for every next column. The result is a whole pic with every 
  other line painted. 
- back to first column, but this time start at line 1 for 
  every other line (1, 3, 5, ..., 85). Again full column height. 
- same for every next column. Picture complete. 

The colours 1 and 2 used for a pic (white and black are always 
the colors 0 and 3) are retrieved from a table at $0d9d. The 
index into that table is the NMxx file number (50 to 81) minus 
$50. For instance for NM50 (inside guild) this value is 26, so 
red and blue, for NM51 (inside Garth) it is 56, so green and blue. 
The files NM82 to NM90 (or higher) have fixed red and blue.
That table you find on the boot disk, depending on whether you have
copies with the pirate slayer removed or intact. If intact, that
list is still x'ored with $EA and you find it looking for "CC BC CC".
The first CC is also the first 26, resp. later $0d9d,x with x = 0.
With copy protect removed you simply search for 26 56 26, the first
26 again is the first offset.

The biggest animation I found in BT1 is NM74, the treasure, 
with the main frame plus 13 animation frames.

 
Last edited by ZeroZero on Thu Apr 29, 2010 9:44 am, edited 15 times in total.
User avatar
Darendor
Posts: 1502
Joined: Wed Jan 14, 2009 1:53 am
Location: Red Deer, Alberta, Canada

Post by Darendor »

That's some pretty amazing work. So these offset tables are hard-coded in the engine memory then?

Where I'm going with this is that it'd be nice to have a picture-maker as part of the editor suite.
User avatar
Twoflower
Posts: 128
Joined: Thu Mar 19, 2009 12:40 am
Location: Haarlem, NL
Contact:

Post by Twoflower »

I agree. This is some amazing work by Tedious and ZeroZero. Since this graphics format has its roots on the Apple-II, dare I bet there is a common painting-program (as Koala Painter on the C-64) that handles the graphics data in the way described above, minus the animations.

I guess I could try make a replacement-image manually - but writing a converter based on 76X86 PNG images with four colors should be doable. This definitly opens up for customizing the BT1-engine.
/Twoflower
User avatar
Darendor
Posts: 1502
Joined: Wed Jan 14, 2009 1:53 am
Location: Red Deer, Alberta, Canada

Post by Darendor »

Hey look, Twoflower came back.
User avatar
ZeroZero
Posts: 286
Joined: Tue Mar 10, 2009 9:10 pm
Location: Germany

Post by ZeroZero »

Presently I am converting my VB6 prototypes to the QT SDK, an excellent cross-platform SDK with a fair IDE (QT Creator) and the advantage to support in WIndows the excellent (and best ever) .Net IDE. I do not know any IDE that is better than that one (Eclipse is lacking major features in the non-commercial version, NetBeans is full of bugs, QT Creator lacks most essential features). I have not yet tried Code::Blocks.
User avatar
Darendor
Posts: 1502
Joined: Wed Jan 14, 2009 1:53 am
Location: Red Deer, Alberta, Canada

Post by Darendor »

What? :?
User avatar
ZeroZero
Posts: 286
Joined: Tue Mar 10, 2009 9:10 pm
Location: Germany

Post by ZeroZero »

This means, that I am checking the chances to port my
VB6 stuff to C++ with the intention to be able to provide
it as portable applications, i.e. as programs that do not
require any installs or setups or pre-installed stuff on a
PC running Windows to be executed.
User avatar
Quantum Reality
Posts: 91
Joined: Mon Mar 15, 2010 8:34 pm

Post by Quantum Reality »

That'd be handy. I found out by accident that a VB app I coded up for someone needed the .NET 3.5 runtimes. I was less than amused.
User avatar
ZeroZero
Posts: 286
Joined: Tue Mar 10, 2009 9:10 pm
Location: Germany

Post by ZeroZero »

Well that then would be VB.Net. Of course any .Net application requires the .Net framework.
User avatar
Quantum Reality
Posts: 91
Joined: Mon Mar 15, 2010 8:34 pm

Post by Quantum Reality »

I was using VB 2008 I think, so yeah. Here's hoping you don't need to make us install any of that. 8)
Post Reply