Reliving the days of playing and programming the game Ultima 1 on the Apple IIe computer. This will be a journey of discovery and creation to build from scratch the entire Ultima 1 program in Applesoft Basic!
Well I figured out the new programming method to compress
the data for the entire world map that saves me a huge amount of bytes.The screen shot above is from a BRUN of the
new program with about 4 new subroutines that work out the compressed data
strings.
The Ultima 1 world map is [4] separate maps that are side by
side totaling 180 tiles wide by 90 tiles deep!
Previously I would have had to spend 180x90 = 16200 bytes to
create the world map!!! "This would not work by the way!"
Now, the total amount to bytes I will use for the entire
world map is approximately 90x15 "15 is the average amount of byte
groups" = 1350!!! That is almost a 15000 byte savings!
The new compression program works as follows:
1. Since each world map line has different types of tiles and
different amounts of each tile I used a value at the beginning of each data
string that represents the total number of bytes in the data string.
Previously I was going to have to enter 180 total bytes for
each of the 90 map line but now I setup each map line as follows:
So the $0C represents the twelve total groups of bytes,
which will be used in a loop checker. The twelve bytes represent 15 ocean tiles
each per byte group!The 9 is the # that
represents an ocean tile/shape.
2. I initialize values for ROW and COLUMN, which for testing
start at 0,0.Since I have ROW start at 0 I end up looking through MAP1 data
string first.
3.I have a subroutine that first reads the initial byte,
which in this case is the $0C and places it in a variable.
4.Then I read the data string again and place each of the
seven bytes into an array called PREPMAP,X with the $0C used as a loop checker.
5.I have a subroutine that reads the first byte in the
PREPMAP array and stores the value in two variables: HBYTE and LBYTE.
6.I have another subroutine that shifts the HBYTE to get
only the F only into the byte.
7.I have the next subroutine shift the LBYTE to get the 9
only into the byte.
8.Now I use HBYTE as a loop checker and place 15 ocean tiles
in a TMPSTORE array that is 180 byte long. This subroutine loops again through
all 12 bytes, again through the HBYTE and LBYTE and places them in the same
TMPSTORE array.
9.Once all 180 bytes are stored in the TMPSTORE array I use
another subroutine that gets the value of COLUMN and retrieves that 20 bytes
starting at the column value and stores them in an array called MAPSTORE.I run
through this process with a loop 10 tiles to get all 200 tiles reference
numbers.
10.Now that I have all 200 tiles I was able to use my
existing draw subroutines with just a number of tweaks. The tweaks consisted of
drawing out the tiles by reading in groups of 20 so I could keep the draw pattern
as a top to bottom approach as 10 tiles down and 20 columns to the right.
I know I can clean these processes up and make them faster
and cleaner but I just need to spend more time on them and refine each one.
So now I am spending my time recreating the entire [4] maps
in excel with all the inidivual tiles images placed in individual cells.The world map will be 180 tiles wide by 90
tiles deep.
When this is complete I will be able to enter all the data
strings for all 90 map lines and move on to the ship movement part of the world
map.I bet you though I forgot about
this movement process that happens on the world map.I have a good idea how to get this to work
and not add any really code to the program.It will add another shape to the program and new parameters for the
movement checker.
Hello everyone,
Well, you have all probably been wondering what the heck I’m doing and what type of plan I’m following. So far I’ve been spending most of my time trying to figure out how to program the movement part of Ulima 1 and boy has this been insane! Trying to learn a new language is hard enough but trying to figure out how to re-engineer a game that has little to no information on it is near impossible. I’ve had some major breakthroughs and some downfalls as you have seen on this blog but things are progressing and I feel as if I’m on the right path. My plan for this project is to take each main part of the game and program it in sections. As each section is completed and functional, “key word here” I will move on to the next main section of the game. As I see it, there are 12 sections to the game:
1. World map movement
2. World map monster encounters
3. Dungeon movement
4. Dungeon monster encounters
5. Castle and town movement
6. Castle and town guard combat
7. Space movement and encounters
8. Big Boss encounter
9. Monster encounters and combats functions – Total combat system
10. Character stats and progressions
11. Title images and menu systems
12. Buying, selling, and stealing items
13. Creation of all shapes/tiles that are used in the game
14. Missions, assignments, and requests from kings
15. Anything else I forgot!
The sections I see as being the most involved will be the monster encounters/combat system, dungeon movement, space movement, and mission, assignments, king requests.
Like any program these seem to be subroutines within the main program and it becomes an issue of space as well as functionality. The hard part for me is trying to figure out how much is accomplished in assembly and how much is in basic. My assumption when Richard was programming this game was very familiar with basic at the time but not so with assembly. I understand he had help with the assembly part by a friend named Ken. Ken did the tile movement part of the game and probably showed Richard how to program other sections of the game in assembly to help make the game faster and more efficient.
I know that certain section will be created in assembly and loaded into memory just like the world map movement section.
No matter what, you will all be informed and updated as to the progress of each section and when they are complete. I have posted my email address, which I will do here again so anyone can contact me for questions, program files and code or even to just leave comments.
Today I will be reviewing one of the Assembly books I’m using to help recreate Ultima 1. The book is titled “Apple Graphics & Arcade Game Design” by Jeffrey Stanton. Right off the bat I will say this book is incredible! Without this book I would never have figured out the correct method of placing shapes on the screen in any location using bit mapped graphics and lookup tables. This book is definitely for the advanced user because it assumes you have some previous knowledge of the apple memory addressing and an understanding of how the apple memory works. It does take a progressive approach in that it starts at a low level with topics covering screen layout, screen switches and controls, colors and background fill, graphics animation using shapes tables and character generators. It starts with Lo-Res graphics and continues on to Hi-Res graphics, Bit Mapped Graphics, Arcade Graphics, and Games that scroll. There are a total of 8 chapters and the book has 288 pages with an index. The book was written in 1982 at the peak of apple graphics arcade programming.
The table of contents is as follows:
Chapter 1 – Applesoft Hi-Res
Chapter 2 – Lo-Res Graphics
Chapter 3 – Machine Language access to Applesoft Hi-Res routines
Chapter 4 – Hi-Res Screen Architecture
Chapter 5 – Bit Mapped Graphics
Chapter 6 – Arcade Graphics
Chapter 7 – Games that Scroll
Chapter 8 – What makes a good game?
Each chapter is well written with detailed information and program listings. Jeffrey gives examples in Basic, machine code, and assembly code throughout the book. As the chapters progress so does the assembly code for each program. In the chapter covering Lo-Res graphics he gives a great example of a breakout program with ideas on color, paddle control, and memory addressing. But the meats of the book to me were the chapters on Hi-Res graphics and bit mapping. These chapters alone would constitute the price of the book! I learned so much from reading these two chapters that I now have a much better understanding of how I place a shape on the screen, just how each memory location is setup in Hi-Res graphics with regard to each line on the graphics page 1 or page 2. He explains how you can use table lookup with indirect indexing to help place shapes anywhere on the graphics screen faster than if you tried to address each one separately. Each chapter also includes a flow chart to help you understand how the program works before you see the actual assembly code. This is incredibly helpful because the Assembly programs in the book are complicated. You have to read through the program a number of times just to get a sense of how things are working. What is nice is that he writes his programs in a no nonsense fashion, where they each have a clean top to bottom flow. Don’t get me wrong, these programs are advanced and you need a high level of understanding of HEX and indexing. He also covers paddle usage, scroll graphics as in the game defender and space invaders. The chapter on Arcade Graphics covers explosions, collisions, steerable space ships, scorekeeping, and page flipping. Page flipping is especially important to help with fast screen graphics manipulation. This process allows you to draw an image on page 1 then while the use is looking at page 1 you draw another image on page 2, flip to this image to show the change, then you draw again on page 1 and flip back to page 1 image to show another change to the user. This generates a smooth graphics frame sequence of moving images without the delay or flicker. Jeffrey uses a lot of Remarks to help give a better understanding of the program while you are typing it in. The programs in the book look like they were programs that were assembled and printed out, giving the user a clear, error free code listing. One of the best parts about this book is that I was able to assemble all the programs in DOS TOOL KIT with very little manipulation of the code. They complied and started with remarkable ease. He has a very clean style of writing and gives a lot of detail concerning each topic and discussion. The programs are well written with lots of comments and remarks. Again I will say this is an advanced assembly programming book but it is still manageable if you give it a lot of reading time. This book covers everything you need to know to get you comfortable with programming in apple assembly. If you want to gain a better understanding of how the memory is used to program games and learn how to program the apple in assembly language then run don’t walk to eBay and buy this book! Be aware that this is a rare book that people are charging an arm and a leg for so don’t get burned by a ridiculous amount. I would say that if you are serious about learning apple assembly language programming and want to follow this blog to learn how to program the Ultima 1 game then you need this book! If you see it for under $50 then buy it because you will not find a better programming book out there that covers all you need to know about arcade game graphics and design.
Price - N/A Date: 1982 Rating is on a 1 to 10 scale with 10 being the highest.
Hello again everyone and welcome to another blog regarding Ultima 1
revisited, where I try to figure out how Ultima 1 was programmed and recreate it
in assembly from scratch!
So, as of today I finally figured out the movement part as you can see
in the video above and it is a lot faster than the previous movement video
test.
This was a bitch! I mean, really!
REALLY!!!
Working with the DOS TOOL KIT is a very slow and grueling
process. Each time you compile your program you have first save
the file which takes 5 of so minutes depending of the size of the file!
Plus you have to wait about 10 or so minutes for it to compile after you
save it. Uhg! By the way, you better save first
before you compile because the assembly editor moves the compiler into memory
and by doing this it erases whatever program is in memory at the time.
Found this out the hard way!
Well as the title alludes to there is a catch or snag in this
programming code! Yes I figured out how to move a character around
the map, which by the way checks collisions with mountains and oceans!
When I was working out the code for the test I only used a small portion
of the upper left section of the Ultima map to do my test. Once I
realized I was able to get it working I started entering in all the data points
for each tile on the map from the left to the right! Well the
problem with the Ultima 1 world map is that it is made up of 4 individual maps
or kingdoms, which are only access by having a ship.
So let me take you back a few days when I was on cloud nine
thinking I’d just figured out how to cure cancer figuratively.
“Yes, programming in assembly is that hard!” LOL.
So here I am with a big smile on my face when I start
entering in all the tile date values for each map line from top to bottom and I
get to about line 24 out of 80! I hit enter to input the line and
bam… I get a memory full error! What! Are you
kidding me! I’m not even half way with the map tiles, let alone
the rest of the ultima program and I run out of bytes! Well, in
the midst of committing seppuku I realize I better go to the one source that can
bring me to the light! BETH!!
Here is what she had to say in an email I sent
her:
Hi Beth,
I bet you never though this email would
come but finally I figured out how to move around the screen and it look and
feels just like the game.
Funny thing is I didn’t use the method you
mentioned where you scroll with the 90% of the existing tiles and just generate
the last 10%. I used table look ups and since the redraw is fast enough in
assembly I was able to redraw using a column or row shift based on the key
pressed. When the key direction is pressed I just re-read the new map tiles and
redraw them all on the screen. I know this isn’t the correct method but I tried
to work out the other method and kept hitting a brick wall! I will try to work
it out but for now it works like it is suppose to. I will post a video very soon
so you can see the progress. I’m totally jazzed! This is huge for me and I can
now work on the other parts of the game.
Hey, however it works is good enough. When
I did my version of U3 I didn’t do the 90% shift thing either, because as you
note just redrawing from in-memory tiles can be fast enough. If you don’t need
the extra speed, don’t sweat it. You can work out the math on how many cycles
per tile it will actually save you to determine how much speed boost you’ll get
– I could believe anywhere from 4x to none at all, depending on how you
implement the algorithms. Congrats again on getting it to draw!
I do have a few questions if it is ok…
1.
I worked out the map and the entire map is about 180 tiles
wide X 180 tiles deep approximately. Well, this is a lot of bytes to use and if
I calculated right that would need 32,400K or bytes for each tile value!!! How
is this possible? The map is huge and basically has 4 separate maps that make up
the entire global map. I have to make sure that Richard didn’t use the back side
of the disk for game use and put some of the maps on the back side. Even though,
how would I put all the individual byte information for each tile to be
represented if we are talking 8k for each map and 32k total?
Okay, I don’t know exactly how he did U1,
so I’m speculating. I can give you some different approaches to try, though.
Conceptually, you should consider the Disk Drive as something akin to a swap
file – it’s like extra memory you can use, it’s just slow to read/write so you
want to avoid it. That said, what happens is that loads are done at critical
points to swap out data files and program code – thus allowing one to
essentially be in an entirely different program when in a dungeon, say, than up
on the surface.
So, swaps can occur on the surface as well,
when you move between maps. That’s one point – so you’d only have to have a
single 8k map in memory at a time, and then you’d swap in the new map when you
approached the edge of the current one.
Also, how many tile types are there on the
map itself? 15 or less? Then you’re wasting a lot of bits. You should make your
maps nibble-based. The top 4 bits of any byte are tile A, the low 4 bits are
tile B. That means you only need 90x180 = 16.2 k for the whole thing and 4.05k
for a single map. And that’s storing it RAW instead of RLE which I recommend
against. See my later note for more on that topic.
2. How do you separate assembly code if you
run out of space in a certain memory region? If I start my assembly program at
$6000 and use the entire free memory range up to $95FF, how would I continue
using the rest of memory for my database of tiles?
You separate out your data and routines by
how big they are. Then you figure out where in the memory map you want them to
go. You then can use Basic to do some like BLOAD to load the individual
data/code files into exactly where you want them in memory.
This tile memory usage for the maps is mind
bending and I just don’t understand how he was able to put in a database of all
the tile references when they would take up so much memory!!!
Another possibility is that he used RLE
compression. That will pack it down tremendously. RLE stands for “Run-Length
Encoded”. It works like so…
And if I’m nibble-based, I can ditch all
the zeros and pull this down to:
31 21 44 25 A1
So…5 bytes now take the place of
22.
The longer the runs, the better compression
you get. So taking a guess that you get 4x compression, that should pull your
maps total down quite a bit. I bet you get better compression than that,
especially if you can use nibbles instead of bytes.
Also, if the map uses a major portion of
the disk how do I put in the towns, castles, and dungeons? This seem like I
don’t have enough space on a single disk but Ultima 1 only comes on one
disk.
Compression algorithms.
J
Joe
So now I’m at a cross road because I
realize I have to change the program I just spent months on trying to get
working correctly and I have to utilize a new set of subroutines that will read
compressed data strings. This is definitely going to change a lot
of my program code.
Well I’m off to work on the problem but
while I’m doing that please enjoy the video and if you have any questions please
email me at:
Hello everyone,
Well, since my last blog you all saw the movement test and can tell that
this method is not functional for a game. So I've been testing some new code
and it is working a lot faster than the last program. So far I'm able to draw a
tree shape across 20 rows and 10 columns with almost instant draw time. I’m basically storing each byte, of the 32
bytes that make up a set shape, directly into the memory region that make up
the Hi-Res page 1 screen. So, it kind of works like this; you have a memory
region that starts at address $2000 for the top-left corner of the screen in
Hi-Res page 1 graphics. This address makes up [1] byte of screen memory. I
store the first byte of the tree shape in address $2000 and then store the next
byte of the tree shape in address $2400. This basically places each byte
starting at $2000 and I go down a line to $2400, to the next line $2800, and so
on... When I get to the 16th line I step to the right one byte and start
drawing the rest of the shape in the next 16 lines. So I use 10 separate
subroutines for each row and these 10 subroutines loop through for the 20
shapes.
Due to this new draw process I had to lay out the shape byte values in a
different way from before.They are now
set so the first 16 bytes are for the first 16 address bytes of memory and the
next 16 bytes are for the next 16 address bytes of memory. So the first two addresses are $2000 and $2080…
the next two are $2100 and $2180 then so on…
I was able to figure out a process where you use an address such as $2000,Y
The Y is like an array that lets you increment one place to the right as you
increase the value of Y. It works really cool!
I have this working but it only draws trees across the entire screen. I need
it to draw separate shapes within the entire screen to make sure it works
correctly. I will post this code and video soon to show just how fast the
screen draw is.
I will work on the subroutines that draw separate shapes from the map
section.
Talk to you soon,
Joe