hello! I's Keith here and welcome to Lesson one of my platform specific
tutorials... and what these are going to do is each lesson we're going to take a
specific task and we're going to learn how to do that on each of the different
hardware platforms we're going to be looking at, and obviously each system
will have its own different firmware and hardware requirements so that task will
be different for each machine - but we were going to write common functions
that we can call and use in the same way on different systems... And today we're
going to be looking at basic text and screen functions, so what we're going to do
is we're going to create a clear screen function... we're going to create a locate
function which will move the cursor... some print character, and print string
functions, and we're going to create a function to read a key from the keyboard
and we're going to do that on the Amstrad CPC which you can see up here in the top
left... The ZX spectrum in the middle here... The Enterprise 64 or 128 - top right...
bottom right we've got the MSX here and on the bottom left we've got our most
simple system which is the TI-83 calculator... so we're going to create one
common piece of programming code which I'll show you here... so this is a common
piece of code that runs on all the systems and just by changing....
the compiler directives here we can enable one of these to compile to that
system... and the way this that we achieve this is we have written
specific code for those platforms that deals with the
intricacies of those systems, but works in a common way so that our common code
can use it without worrying about which hardware we're compiling for, and that's
really the secret of creating a good multi-platform piece of software without
too much debugging... to create modules (kind of like drivers) that will deal with
the hardware and then just use those drivers in the code... now we're going to
be doing much more complex code today so unlike last time I'm not going to be
typing in as we talk through it, because the debugging tools on systems other
than the Amstrad CPC aren't as easy to use so it's probably not realistic and
also we're going to cover a lot of code in relatively short amount of time
so let's begin... first of all let's just have a look at
what our program is actually doing... so you can see here first of all we're
we're clearing the screen and then we're setting the cursor position and we're
using HL for the cursor position and H is X and L is Y in this case and the top
cursor corner is zero so top left is zero on these machines
now obviously the screen size of the Machine... it varies quite differently. most
of them at least 32 characters wide but on the ti-83 it's
not even that (16x8) ... so we can't really do much about that but we can at least make sure
that the top corner is (0,0) and then what we're doing with doing a
PrintString which is message and like last time the the PrintString command
is virtually identical to last time... so it's character 255 terminated.
that's our message... so we print that to the screen, we do it three times... moving
the cursor each time just to get this kind of ladder effect here
and that's just to prove that the locate command is working correctly... and then we
tell the user to press a key... we use this new line command to move down a
line then we wait for a character from the user and we print back the character
that the user entered and then we've got this shutdown and start init command
which are doing some stuff that those systems need to get them ready for a
text screen... Now. this tutorial is going to be using the firmware to do all of
these operations generally I don't advise that if you can avoid it because
using the firmware means that you've got more risks involved with regards to...
"what is the firmware doing with you registers?" you've got memory limitations... "what banks
can I page in and out?" and thing...s but for this kind of beginner lesson we want to
be able to get things working as fast as possible so we're relying on the
firmware to do most of the work for us... later on when we get more advanced we
will be able to create our own sprite font... where we can create our own common
font for all of these systems... send graphics to the screen in a common way...
we can create our own input routines and if we want we can mimic
the same names of the functions (we have here) and the same functionality of the functions so a
Sprite based locate routine works identically to our firmware based locate
routine that we're going to be learning today
so starting simply with the firmware and then once we're more confident...
once we've got more skill, and more software we can break out into a much
more advanced routine that uses sprites to do the fonts in the same way... so I
don't think it's too much of a problem that we're starting using the firmware
today... let's begin anyway and let's have a look at the Amstrad CPC version
so I've created a kind of structure for my directories here so if we have a look
you'll see you've got these SRC folders and these are the source and these are
what we're going to be looking at today... RES stands for resource there aren't
really any of these but that would be things like sprites or music... BLD is
temporary files relating to the compilation (build) of the code and then REL is
for release which is the final disc images... the final tape images for the
software itself... now the compilation scripts I'm using
here I'm going to try and make available to you unfortunately they're not quite
finished yet because it's taking a lot of testing and configuring to get it to
it working right on everyone's machines but I will make this available to you so
please look at my website... because I'm hoping it will be on the website within
the next week or two so... anyway so we're going to go into SrcCPC here and the
the one we're going to look at is "functions" because this is doing all of
our basic firmware functions that we're going to look at today... so here is the start
of the file and you can see with regard to the CLS command we're actually just
using a firmware function call at &BC14 and because we're calling this rather
than creating a label and a CALL command we're actually just using a symbol
here and defining CLS as being memory address &BC14 in the firmware and that
will do the job and PrintChar as well is the same we're just using this &BB5A
function call and WaitChar as well... we're using &BB06 so yeah... we've got three
of our tasks done immediately with just three lines and no real code at all
just definitions! now, locate the firmware call is slightly different... the top
corner is actually (1,1) because we want it to be (0,0)
we're having to INC H and L before we call the firmware function
call and then we've got this push pop here just to protect the value of HL so
that when we return back to the code ...that required change by the
firmware has been hidden from the program itself... and then NewLine on the
Amstrad CPC... as most systems we use a character 13 and a character 10 which is
carriage return a new line and there we go! and then with PrintString... this is
exactly the same that our previous time that we've looked at this
routine in the main beginner lessons... so we just loop through the
memory just passed was in HL... we print each character until we come to a
character 255 at which point we return. And that's everything that the Amstrad
CPC version requires! Bow, part of the reason it's so simple on the Amstrad CPC
is because i write all of my code first on the Amstrad CPC, because as i've said
before... I found WinAPE the easiest assembler debugger and emulator for a
beginner to use with programming so I try and make it easy on myself I work on
the Amstrad CPC first and then a branch out onto the other systems where maybe
the debuggers aren't quite as powerful... and certainly there isn't a built in
assembler! so let's move on from the Amstrad CPC now, and let's have a look at...
Let's have a look at the Enterprise next
so here's the Enterprise version... now the enterprise is here.... this is a rather
obscure 8-bit system from the 80s... it didn't it wasn't very successful in the
UK but it probably should have been because it's quite powerful! so I've
been trying to have a look at it, and I'm hoping to include it into these lessons...
and I'm hoping to release my game on it one day! so let's have a look at it now...
The enterprise has a fairly advanced operating system built into it and it
works in something called "channels" which is where you open a connection to
a device (such as the keyboard and/or the screen) and you send data to
it or read data from it it very much like the sort of a dotnet (eg C#) stream
functionality... so it's quite different to the Amstrad CPC version
there's a lot more code and there is a large init routine for getting the
screen working which we're not going to look at today... but anyway let's make a
start on the functions we are going to look at... so the init routine will have
loaded up channel 10 as being the screen and channel 11 as being the keyboard so
just bear in mind that you would need to look at that init routine as well if you
wanted to see this... and all of the source code will be available my website... if
you really want to get into the detail of how to get the enterprise screen
working please look at that, or just look at my "Enterprise hello world example" which uses the
same code that I have put onto YouTube before... so PrintChar... we protect BC
because we need that to stay the same to maintain comparable
functionality to the Amstrad version... then we load the character that we want
to print to the screen into the register B we load A with the channel number and
then we call RST 6 which is sometimes called EXOS in
people's examples and even the debugger actually does that the reason for that
is they've created a macro to replace EXOS with rst 6... now I don't tend to do that
because I want to show the real commands that are running... but that's just
something to bear in mind if you see other people's examples you might see
RST 6 called EXOS... so we just call that and then the Byte afterwards is a
command number and so command number 7 says
"Write a byte to channel A" and that's all we do for PrintChar that's not
too bad... PrintString is basically the same as before the only difference is
the PringChar command I've replaced with these commands here and
that's just to do the equivalent of this... but I've embedded it in the print string
command so that we don't do more push pops than we need to... so that's just a
slight change I've made there... and now next we've got the NewLine
command... now on the enterprise new line is basically the same as the Amstrad CPC
we send character 13 and then character 10... now just to kind of speed things up a
bit rather than calling the print char
command twice I've actually created a string of the characters here
and I'm using the WriteBlock command where we can specify the number of bytes
in BC and this maybe is a little bit faster (maybe it's not I haven't tested
it) but at least it does allow us to show you the use of the WriteBlock command
which is where you send a number of successive bytes to a channel on the
enterprise... now WaitChar... we'll wait for the user
to press a key and return it in A... on the enterprise
we've got channel 11 as the keyboard and command 5 will read a byte from the
channel and store the result in B... now because our Amstrad CPC version needed the
byte returned in A I have had to copy B to a and then I'm using this Push Pop
here to just protect the BC register from being affected so that this version
matches the Amstrad CPC version... it's quite nice actually the enterprise
operating system... this EXOS call doesn't alter any of the registers at all which
makes our life very easy but unfortunately because you're
having to use A for the channel number and then B for the data... even
though it's not altering any of the other registers it is affecting more
registers than the Amstrad CPC version did (which didn't affect any at all and)
when we're working with code we're going to use in a common way across multiple
platforms we need to make sure we know which registers are being altered... which
have been corrupted and which are being kept the same because....
we need to know which ones we can rely not having been changed by the functions
we were calling.... okay so that's our WaitChar routine now our CLS (clear screen)
routine is a little bit obscure... this is again done by sending text characters to
the screen and it's kind of some function characters so we send the
escape and then this CTRL-Z character here which are represented by
&1B and &1A so just like we did with the newline we're sending a block
of two characters to the screen on channel 10 and that will have the effect
of clearing the screen... a little bit strange! but it does work just fine. now
here we've got a definition of our WriteBlock command here... probably actually
not worth calling that - I could have just put that in there, but I copied this from
another example so it's ended up in there like that.
now our Locate command... a little bit more hectic here, maybe. In the same way as the
clear screen command... we do the locate by sending a string of four characters to
the screen... so the first one is this escape code and then actually it's an
"=" symbol... but it never actually appears as an equal symbol...
The screen channel will process it and know that it's a re-location command so
the equals will never appear and neither will the numbers we send following... so we
send "ESC =" and then Y and then X and that will ....
move the cursor to the correct location on-screen, and then show following text
in that location... so because we having to to send four characters rather than
doing four print char commands (which would be quite wasteful) we've done this
WriteBlock of we are four bytes and then we and then that will do it but
because we've used so many different registers here now we are
actually having to protect quite a lot here.. so yes that's that's not ideal but
never mind! and also quite strangely, there's some special rules
for the locate command on the enterprise... so on the enterprise we add &21 and
that will make the enterprise locate command work the same as the Amstrad CPC
locate command, which is what we want to do...
for this sub function to work with in the same way with our other systems... and
here we've actually got all of the rest which I'm not going to cover today this
is all relating to initializing the the enterprise screen memory... I did cover
this a little bit more in my "hello world on the enterprise" example and we will
probably look at it later in more detail but today I just want to focus on these
common commands on all the systems... so that's the enterprise version... let's have
a look at the MSX version.... now so on the MSX you can see we've got PrintChar
which is just a firmware function at &A2 which is in the low memory area
and then we've got WaitChar which is at &9F... again two very simple commands
which actually do the exact same things on the Amstrad CPC so we're sorted very
quickly there... and screen isn't too bad either... the only thing with the clear
screen command is it requires the accumulator to be 0 so we've done XOR of
the accumulator... which is our quick way of loading the accumulator with 0... then we
jump to &00C3 which is the CLS command so... that's quite easy! &00C6 is the locate
command... but just like on the CPC...the top cursor co-ordinate
is (1,1) so to match our expected functionality of (0,0) we
INC H and L and then we're just calling that firmware function and then
we're protecting H and L just to make sure that they return from this locate
command in the same state as we sent them... NewLine on the MSX is a
character 13 and a character 10.. so we're just using PrintChar to do that and
PrintString - very simple - just using PrintChar over and over again... that's it!
so that's nice and easy... shutdown is disabling interrupts and halting that's
to stop the system and that's because on the cartridge based systems we can't
return to basic - nothing too
exciting on the MSX... so let's have a look at the TI-83 calculator and a TI
calculator is our most limited system we're covering (it's fallen asleep so lets
to turn it back on)... so this is the most limited system and you can see it's
screen is actually very small (Confirmed: 16x8 characters)
its a very small screen, so obviously
we can't make it work exactly the same as the other systems but we can at least
give ourselves a start by making the commands we've defined work in the same
way... so on the on the TI-83 calculator this RST 5 is often referred to
as BCALL and this is actually why I don't like using these defined macros
because everywhere I went online I saw the command BCALL being used which is
not a Z80 command and they they kept referring to this BCALLl and I couldn't
find it!... and I had to search around for half an hour to find the Include file
that defined what BCALL was... so rather than using
macros and include files, I try for these basic tutorials to just actually use the
z80 commands... so that there's no possible confusion when you're looking at this..
you know exactly what this z80 is really doing... if you want to use those
kind of macros I'm not against them, but for these basic tutorials I want to show
you the real commands... so RST 5 is a BCALL... it's a far call what it does
is...I believe it pages in the ROM of the
calculator and then calls this memory address in that ROM... so it reads the two
bytes after the RST uses them as a jump address... switches the ROM back off
and then returns to your program... so these two bytes are kind of swallowed in
by the RST 5... but it's effectively calling this address in firmware... so that
address in firmware will clear the TI-83 screen... and then our Locate command here...
well the locate commands very different... we don't call anything at all there are
two variables in memory at these addresses for the Y and X location of
the cursor we just load them in and that does it all for us... so I guess next time we
do a print command, the TI-83 firmware will look at those addresses and go "well
that's where I was printing to last" so that actually does the job!
for PrintChar we've got this command which does do the job... and we just
need to protect all of our registers because the firmware will overwrite them
if we don't... so to make sure when we return back to our program we
have common functionality with that print char command on all of
systems we need to protect these registers... WaitChar as well... there's a
nice sub firmware function here... unfortunately though because of the odd
way the TI-83 calculator's keyboard works you pressing number one
won't necessarily come up with a number one and things like the letter A you
actually have to press shift and press it... you would actually have to process
the commands more thoroughly... you'd have to read back what the key had been press
and then translate it based on what numbers whether you expected... numbers or
letters... so we're kind of doing a very crude job here but I thought it was
enough just to get started and on a later lesson I've actually written a
joystick input routine which works on the TI-83 calculator and which has a
redefined Keys function... and then emulates a joystick. so if we're looking
to do gaming then we don't need it... but if we were looking to do text input then
you would need to have some handling of that some alpha shift key to allow you
to get text or numbers depending on what you needed to do... so a NewLine... we've got
a special new line function here sending character 13,10 does not work... sending
character 13 doesn't work... you have to use this special NewLine function so
that's a good reason; on the TI-83 calculator; why we've got a
function to do our NewLine command and not some kind of defined
character string that we sent to the PrintString command because on the
TI-83 the PrintString command cannot do a newline character... and then here we got
our PrintString command... which just does the exact same as on the other systems
just repeatedly uses the PrintChar command to get the the text on the
screen and there we go! that's the TI-83 calculator... now, considering what a
strange system it is compared to others and what a limited system it is it's
actually quite minimal, the amount of code we've had to do to get it
to work... it seems to have a quite powerful firmware even if it is heavily
limited in screen space and RAM... so finally let's have a look at the ZX
Spectrum... so on the spectrum we have a firmware call to do a clear screen
it's at &0D6B... the locate command is very much like the Enterprise version,
we're sending special character codes to the screen
and the first one we have to do is send character code 22 which... it's rather
strange on the spectrum... you may have seen a spectrum keyboard that it
has these... I'll show you... so you'll see on the spectrum the keys actually have
these commands on them and each of these commands is actually a
character in the character set... which is kind of odd but that is the way it has
been programmed to work so what we're sending is character 22 which is
actually defined as the "AT" character if you will... so the screen firmware
reads that character and then understands that the next two characters that are
sent are actually going to be a screen location and then moves the text cursor
to it and that's what we're doing here... and then again we're just protecting all
of our registers so that we don't cause any negative effects
to the rest of the program when we're compiling for the spectrum version...
now. NewLine on the spectrum is just a character 13... there's no character 10
it doesn't like receiving a character 10... so what we do is we load A with 13 here and
then you'll notice there's no jump to PrintChar or CALL PrintChar... because PrintChar
is actually next ... so we're just kind of letting execution flow through into
PrintChar and that saves us a jump command so that's quite nice and then
PrintChar... we have to open the stream number, we have to open channel 2 on the
spectrum... there are two channels for the screen... there's the top screen
which is the main bulk of the screen and then there's a one line bottom screen
which I believe is down to the spectrum 48k where memory was limited and the the
bottom screen was used for data entry and the top screen was
used for showing your program that you are working on... so we're selecting the
top screen which is the main one and then we're sending the character with
RST 2 here... and that will do the job for us... and PrintString is the same
we're just using that same channel open by
selecting channel 2... we call this function here which will open the channel and
then we're doing a loop here and we just repeatedly running RST 2 to
send the characters to the channel and then when we're done we just restore our
registers here and then we turn back to the program we were running.. now
WaitChar is kind of similar... you'll see here we're protecting our registers as
always we're loading a different stream (channel) ... stream (channel) zero is the both the bottom
screen when you're writing to it.. or the keyboard when you're reading from it so
we select stream (channel) zero and then we open the channel... and then we just call this
Read Byte command which will read a single character into a and then we go
back to our program... and that's the spectrum version done! so there we go!
now you can see here that we've got a few different ways of working with
things.. the spectrum and the enterprise have this kind of channel
stream kind of concept of opening connections to devices and then sending
data to them or reading data from them ... whereas the MSX and the Amstrad at
least don't present that to us... from in the firmware sense, we have these very
simple firmware functions that do all the work for us
and then the TI-83 calculator is a bit different again... it's basically the same
way the firmware does it all for us there's just a few slight oddities on
the TI-83 and I think there is really just down to the fact that it's a
very limited system with very little memory so it just has to sort of make a
few changes to the way it works to accommodate that but they're all very
good... now what I haven't done is I haven't shown you the program working so
let's just give it a go now... so we're running it from the disk because this
the script create onto the disk and into memory simultaneously... so you can see
we've got this ladder of hello worlds and then we if we just press the 8 key,
you're say see it you says you pressed 8... okay so that's the that's the CPC
version well let's just have a look right here let's recompile the TI-83
version and just to prove it's actually compiling I'm going to change this to
"hello world 777"... so we'll just compile that so you can see saved to Bld'TI\Program.bin
and now if I get up my virtual Z drive here and then if I run TI_Go... I just hit enter
here hello world 777...
okay so you can see that the re-compiling has just worked and then
if I press the eight key you can see it doesn't come up as an eight and that's
because it's coming up as a key code... so you'd probably need to create some kind
of translation table here... but anyway I think that was an adequate beginner
example of how to read the ti-83... let's just do the Enterprise version
here as well... so if I just rem out this TI-83 and then this build
Enterprise and we've got two options here to run from a disk image or from a
from file emulation... so we'll run the disk image and "hello world" .... you press
V... so there we go it's like that that works as well... and again the
important thing to just understand is all I'm doing here is removing the
comment from one of the lines in this listlike the ZX spectrum here here
commenting out the others... and that tells these scripts here to load
in the correct functions to control that systems firmware... and because those
functions work in a common way all of this code here is absolutely identical -
never changes - and that allows us to write one program that works on multiple
systems in the same way, and that's going to be essential if you want to develop a
game for multiple systems because it's going to reduce your debugging time...
and your development time just by miles and miles... so it's definitely the way you
want to try and program if you want to program for multiple systems, and I mean
of course you're going to have your own time constraints.. and your own
goals in programming. But for me... if I can write one program and release it on five
systems then ...... that increases the value overall of the
development time that I have to invest... in that the result is five times
(that of) if I've done it for any one system... but the development time is
probably only two times the actual development time of just working for one
system... so for in my opinion for me it's definitely worth aiming for more systems
if you can (if you're interested in it of course!) there's no point if you
don't like these other systems... but I think it's good and the other thing is
if you've written your code in a way that works on five systems you can be
pretty confident that your code is it's designed solidly... that it's bug free
because you're kind of putting it under a stress test ... where if you've made any
mistakes in your code, if your design has any flaws in it - any weaknesses in it.. you
will find them because you will be unable to get it to work on the more
limited systems ...or your code that worked fine on the Amstrad... maybe it was
corrupting a piece of memory you didn't know about and that piece of memory may
be far more important on the spectrum... so by developing for these multiple systems
you will really push yourself and you'll really see if you your code was as
robust as you thought it was.. because you will find out that - well it doesn't work
as you expected!... Now as always, the source code for today's lessons is going to be
available on my website and very soon I plan to make all of the development
tools available on my website.... I just haven't finished the debugging yet so
please visit my website... there's a new page for these tutorials they are split
off and the main ones.... these are called the "platform specific series" and the
the documentation is very much the same as before so we've got
all of the versions of the program here with details of how they work just like
we've covered today... so please go and take a look at that... please download the
source code.. let me know what you think! I'm planning to do more of these as soon
as I can... but they are very time consuming, because I'm essentially having
to develop and test five times for all of these different systems that I'm
covering! It won't be possible to cover the TI-83 in all cases because it's
such a limited system... but the other systems I am going to cover every time
so when we come to disk loading or sprite showing you will get an Amstrad
CPC routine, a spectrum routine, an MSX routine and an enterprise routine... that
will work in the same way and that will allow you to target all of these systems
very easily.. and also hopefully I'm hoping that this website will become
good easy access point for people who are looking to do the basics on these
systems... when it came to me looking up the locate command on their ZX
spectrum I had to spend about half an hour searching Google for it for some
reason (I'm sure it's on every website out there but I just couldn't find it)
and I ended up finding my own website before I actually found the answer!... so
I'm hoping that I can make the platform specific series answer the beginners
questions on programming these platforms.. you know how do you do this on the
spectrum... how do you do on the MSX... I'm going to make the answers all there as
easily as possible.... please like this video and follow my channel! If you want
to let me know what stuff you want me to cover in the future in the comments... I'll
always do my best but of course it is very time consuming... so I may not be
able to - I can't do really crazy complex things, especially if I don't already
know the answer! which in a lot of cases that could be the case!... so anyway thanks
for listening today! and goodbye!
Không có nhận xét nào:
Đăng nhận xét