Showing posts with label AMD. Show all posts
Showing posts with label AMD. Show all posts

Saturday, 13 July 2024

What is it with the AMD Adrenaline Software?

Very quick one, I've taken delivery of a new Graphics Card... (oooooo ahhhhh).

And it's a seed change for me as this is my first ever AMD graphics card, so this is of course going in my own main PC, which itself is my first ever AMD CPU powered machine I am therefore all Team Red for the first time ever... EVER!
I have been so excellently impressed with the performance of the Ryzen Zen 2 chip at the heart of my machine and my old EVGA 1080 GTX Superclocked was showing its age.  It was therefore time.

Unfortunately I made a huge mistake... No, it wasn't the model, no it wasn't the price, no it wasn't even paying to import it (because it was cheaper by a margin to have the card fly all the way to me through customs from California than actually buy it here in the UK - Thanks Brexit! - Not).

Anyway, it arrived and I plugged it in, and my monitor didn't come to life; I then found the signal was going to my massive 4K TV (on the HDMI) not my monitor (on the display port - go figure).

That sorted I then needed drivers, so I went to the AMD site and looked and found what it said was the best package to install on Windows 10 64bit for this card....

I don't know ANYTHING about the AMD software, and oh boy is that a mistake.

I installed it and was immediately REALLY REALLY IMPRESSED with it!  Yep, it's beautiful (even if it is backed by Qt and I hate Qt) and smooth and does wonderful things.

Unfortunately, and this is key, it was taking a lot of CPU whilst doing this work... So I closed it... and it STILL took a bunch of CPU, nearly 10%!

Just idle at the desktop, 10% CPU.

I was properly baffled by this, it must be doing something, so I went through all the settings and disabled everything I could see, everything turned off, clean reboot, and it was still taking 10% but now was peaking to 14% on odd occasions just idle!

What the heck, a bunch of goodling and I can find a bunch of folks complaining about exactly this, and one guy properly threatening to boycott their brand unless they explain how to just install the drivers without this software suite.  There was no answer to these please, and they were old posts, like there's been an apparent hard reset of the search results for this kind of question.

I spent a bunch of time trying to fathom this issue to no avail, so I like my forebears in the search results set about trying to find JUST a driver package.  I could not find one,

I therefore just decided to pull the card and possibly return it.  And so uninstalled the AMD Adrenaline Software, it took its time... And really grated on me as it spent about two minutes showing me the text "We value your feedback"... without ever giving me a link or contact in order to tell them anything... and this is during an uninstall process; surely someone when they added this eye stabbing annoying message thought "Oh we need to have them able to give me feedback!" ... Nope, seems not.

Now completely uninstalled and the package opens a website... This website... which I link only for you to see... as it's an advert... for a version of the very card I am uninstalling the software for.... Yes, AMD that's pretty tone deaf, also... WITHOUT YOUR DRIVERS INSTALLED YOU CAN NOT VIEW THAT SITE, so double double own goal there .... Less Advanced Micro Devices and Anyone Might Despair.


Here I am then, flabbergasted, back to a tiny resolution, opening chrome and trying to find just a raw driver package and I can't find one.  My machine has now cooled and I hear the CPU pump has ceased whirring away, as it was constantly with the 10% load on it!

I sat looking for ages when I first installed the card, really ages, we're taking an hour before I went with this Adrenaline stuff.

When suddenly Windows itself kicks into life and installs the Microsoft supplied WHQL driver, and guess what?  Yes, it's the same base driver, and it works absolutely brilliantly... and crucially my machine is not taking any CPU when idle, it's gone quite quen idle, I'm sat now typing this with a YouTube video playing, about 6 chrome tabs open and a copy of Visual Studio Code open too and it is silent, the machine is silent, just as I built it to be (unless under load).

Looking about I see AMD are recruiting software engineers, I have no idea what for, but if they see these pages - I'd suggest a bunch of refactoring is due over there folks.

Wednesday, 10 February 2021

My Desktop PC History

My PC history and I literally mean PC's, so we're talking after my Atari ST, this is a list of the machines I've owned and used in order and a little bit about what I did with them.

486-SX-25 (1994)

The first PC I used was built by a company based in Baseford in Nottingham, it was at college and was equipped with an Intel 80486SX, it ran at 25Mhz, ran DOS 6.2 and a netware network layer.  It had 4MB of ram and basically was very much locked down, that was until we figured out how to quit out of the netware login screen (repeatedly CTRL+C whilst hitting enter fast).  The college had loads of these machines of this specification, the three main IT labs of course but then there were two more directly outside one of the lab doors, another in the head of Chemistry's office and a bunch in the library.

I primarily programmed in Turbo Pascal on these machines, I remember playing about with writing my first games on this in EGA graphics mode.

486-SX2-50 (1994)

My first home PC, it was from Watford Electronics and had their own ISA riser connecting to a dual speed CD-ROM, I remember Encarta amazed me, as well as working out how to just play CD's whilst programming.  I of course did my college work on the machine, and I remember my brother did his too.

The machine came with 4MB of RAM and a 200MB hard drive... MEGABYTE... I remember windows 3.1 ate into the disk significantly.

But DOOM came into view, now at the time I had no idea really what SX meant, but it had effects on DOOM's speed.  One of them was the maths.

I actually programmed around the limitations of t he SX chip without realising it and this came back to save me (or curse me) later.

Later we got Dark Forces and another 4MB of RAM, giving us a total of 8MB in this machine as I went to Uni.

But basically the SX had no floating point unit, it had only integer mathematics as well as a bunch of other operations being missing.  It was basically a DX chip which had failed QA and had come part of it disabled, but this was a "2" which meant it ran twice as fast as the machines at college.  And that speed difference was reflected in my little home brew games suddenly running twice as fast.

This remained my main and only PC going to Uni, but in my second year of Uni, sharing this machine with my A-Level studying brother I needed my own and these "Pentium" machines had been released.

Pentium-100 (1996)

The power of the penium, with the EDO ram SIMM's was so massive compared to the 486, and this was the first machine I started to program in C on.  Indeed, it was also the first machine I wrote Java on and even played with Linux.  But I never loved this machine.

Running at 100Mhz, it was blisteringly fast to me, but I was still using DOS 6.22 and Windows 3.1 for the most part.

I should have, it was the first machine I bought with my own hard earned money, I did keep it until my final year at uni, but by then I had started to build my own machines.... But I'd also been to work in IT.

HP Pentium-166 (1998)

This was a machine at work, it was mainly a windows 95 platform for me to work with, I soon had Windows 95 at home too, so things balanced up.  All I used this for was some very basic database work, which the prior incumbent in my job had left (and which was awful).

Compaq Pentium-II 233 (1998)

Another work machine and at 200Mhz, it seemed like the things of legend, but this was a compaq deskpro to which all the analysts and myself upgraded, with  massive 17" CRT monitors, they were really nice machines.

I did a bunch of Pascal programming on it, a bunch of Cobol and some database work (I think it was in FoxPro).

Pentium-III 300 (1999)

Returning to Uni for my final year I built this machine myself, I remember this was a slot-1 processor, and I put a whopping 32MB of RAM into it.  I wrote my dissertation on it and networked it to my older Pentium 100.

Voodoo3-2000 AGP.... Which I only sold in 2013 on ebay, at a profit.

Cyrix-150 (1999)

This was actually my pentium 100, but I found the Cyrix chip for sale for only £10 at maplins so bought it and stuck it in the old PC, to find it was a massive performance improvement when compiling and running both C and Java.

Celeron-III 450 (1999)

This was a machine I actually built for a customer, I spent a bit of time making PC's and making money doing so in the late 90's.  And this was one which stuck out to me because it was such a nice performing machine, it included a graphics card (I think it was a Voodoo3-3000).

The build was with a gigabyte motherboard using slot-1, but the processor was socket 370, buying it and using a daughter board was cheaper than buying a socket 370 motherboard, and performed no differently.  It actually made adding the cooler so much easier.

Pentium III 500 (2000)

I bought this after uni, just a new chip, with 128MB of RAM as I set off away from uni.  I just played a lot of games on this machine, dabbled with Java, but I didn't have the compiler suite we used at work to my programming was mostly all in the office.

Pentium III 500 (2001)

This was the office machine I had at the time, I'd had one before, but don't recall it, this one however was by the local computer firm in Redditch and I remember it was a 500mhz Pentium III.

GeForce... At some point at home in 2001/2 I got the first GeForce card at home and started to babble with OpenGL on Linux.

Pentium III 1ghz (2003)

Another office machine, this was a bod standard HP I think, the job here was C++ programming, but coming from a pure Borland using history (all the way from college to now) I was suddenly not in a Borland using shop and... well... I didn't stay at this job very long.

Pentium IV - 1.5ghz (2004)

A prescott Pentium 4... at the high speed of 1.5ghz, this was my first hyper threaded machine, it was built from the ground up for gaming... so it had 1.5GB of RAM (the most I could afford)... the dual thread pentium 4, overclocked to 2ghz on air.

A pair of GeForce 8800GTX in SLi.

I bought it to run Battlefield 1942 really well, and was really very good.  I also played national league Day of Defeat and started my dual obsessions with World of Warcraft and Eve-Online on this machine.

Core 2 Duo 3ghz - Wolfdale - 2.3ghz

I built this machine for my father, he didn't appreciate it at the time.

Core 2 Quad Q6600 - 3ghz (2007)

This was my main driver for years, and the machine I first put into the Cosmos 1000 case.  It had various RAM, disk, graphics configurations until the last iteration being a GeForce 260 GTX (squared) so this was two GeForce 260's on the same card in SLi together.... It was really good.

I spent time coding for Lordz Game Studio on this machine, as well as playing lots and lots of games.

Core i7-920 (2012)

Yeah, we're at Core i7.... the best I could accord for the i7 release set and it went into the same case as the Q6600 was in.  This machine was a main-stay until I actually swapped out the Core I7 (4 cores 8 threads) for a Xeon which was 6 cores and 12 threads... I did that in like 2016... so I've had 12 threads at home for a long old time.

Ryzen 5 3600X (2018) -> Ryzen 9 3900X

Team Red.... Aside from the cyrix I've always, always been Intel... Ryzen turned my head, and the 6c12t 2600 was the first I could get my hands on.  But the motherboard was purchased with upgrades in mind and the same machine now has a Ryzen 9 3900X 12c24t.

It was a mchine built around the 64GB of RAM, dual m2 NVMe and a stack of 4TB disks, all in a corsair obsidian case.

Thursday, 28 November 2019

From intel to AMD...

Well, that just happened... I bought a Ryzen CPU...

That may not seem a huge revelation, but let me be clear in my PC's I have always (well nearly always) had Intel CPU's.  But the current 3rd generation of Ryzen has Intel on the ropes and I have to get moving with my new PC build.

And I've gone in what may seem a strange direction.

Initially I thought about third gen Threadripper, but I really can not justify the expense, most of my work is done on servers and though I could bring it all onto my workstation for compiles (like LLVM) but really its a convenience and not worthhy of nearly £2500 (after a processor and motherboard).

So what did you do Xel?... WHAT DID YOU DO?

Well, I've gone with an AM4 socket motherboard, a very good AM4 socket motherboard, and I've gone with a 3rd Gen Ryzen Zen 2 architecture processor, but perhaps not the one you'd expect.

You may expect me to have gone with the Ryzen 9 3950X, and you'd be right sometime next year, when they're properly out and in stock and prices have homogenised some.  But today, pre-Christmas, they're like rocking horse shite and costly.

The processor I've gone with then is the Ryzen 5 3600X...

Yes, it's Zen 2, yes it's AM4... Yes it's only 6 cores and 12 threads like my current workstation machine.  But it was only £200.  It comes with the stock cooler, so I cam practice with that before delving into fitting the AIO.

I've gone with an Asus ROG top tier motherboard for this class of processor, and I plan to let it go up to the 3950X with it's 16 cores and 32 threads sometime next year.

Memory, I've gone with two sets of 2 x 16GB Corsair Dominator RGB.  For a total of 64GB of RAM, which is a massive upgrade and maxes out the motherboard.

For that's the big thing I'm giving up, if I had gone with the threadripper3, then I'd have automatically had access to double the RAM slots (8 to be precise) and 128GB of RAM is common as a maximum on the X399... But here on the X370's it's usually 64GB (though some are 128GB).

The motherboard, RAM and processor all come from Amazon, for (to me) the princely sum of £700.  So the whole new machine, with the PSU, AIO, Case and storage I already have has topped me out at £1,100.

Expect build videos and tech tinkering footage soon.

Thursday, 19 October 2017

Gaming, AMD & HP

Over the next few weeks I'm going to be musing about the connections between the humble concept of the PC, where it started for myself, which processors and upgrade options I've taken, which dead-ends I've been down and where that took my gaming experience.

And where I hope to take my gaming experience in the future.

Today of course I'm running a quite nice Intel i7 processor and an EVGA GeForce 1080 GTX graphics card, however, that rig is nearing end of life for me, only the graphics card is a recent edition, and so as a high-performance low budget kind of chap I will be looking at my options, including the latest offerings from both Intel and AMD and the modern differences in nVidia versus AMD graphics offerings.

The secondary thread from this will be a little retro-grade gaming, this is not going to be full on retro - though I am in the mood for some Carrier Command on my Atari ST - we're going to be looking at retro post 2000 stuff.... Watch this space.

So why does this post have "HP" in its title, well... the community out reach team over at HP have sent me a missive, and though they have a zero budget want to share my posts with you guys in the world.

If you came here from a HP/AMD re-tweet, post in the comments below!

Thursday, 1 June 2017

You know...

You know that moment, when it's been over a week since you blogged, and yet you feel like it's been five minutes as you're so busy... Yeah, I've got that feeling right now...

So I thought I'd take a moment to list a few planned projects I have for these pages, if I ever get time for them....

The first is to look at the Intel TBB library, this is intended to assist in taking full advantage of the multicore capabilities of Intel chips, not something that has ever fully come into my sphere of interested (beyond running threaded code with posix threads or boost::thread or std::thread of course).

The second is some more Javascript, Python and MySQL interconnection code, in order to write a ticket/development control system, this is because I need a new one, I'm a little tired of the ones I use professionally, and the good ones out there on the interwebs are generally paid for, or limited for free, services.  So I want something super simple to handle my tickets... "Super Simple Tickets"... SST... That has a ring to it.

The third is to finally, go through some of my old Atari hardware, I plan to go through a video series just building from all the spares I have an Atari STE and I'm going to sadly sell it, as I have a LOT of junk left around the place.

A fourth might be to put together some AM1 powered AMD based machine, in order to see if the Intel TBB leverages itself smoothly onto that platform, and it appears to be pretty cheap to get entry there.

A fifth is to start recording some more game-play on my brand new EVGA nVidia 1080 GTX SC, which is an awesome piece of kit and has opened up the scope of my game play from my retired EVGA nVidia 700 GTX SC... Yes, Superclocked to Superclocked I have moved, and it was a tear because the 770 still stood up and produced such good results on the games I play, however, Unreal Engine 4 based games coming out over the last year have started to test it, and I never could get good performance in early versions of Ark (before they optimised it) and not at all in the currently unoptimized Player Unknowns Battlegrounds... So an upgrade I had, and nearly £600 later I'm a very happy puppy.  Though the delivery was rather shoddy.

And the final sixth is related to the fifth, to record more about the Medieval Minecraft Fortress and underground themed base I've been building from the excellent book I reviewed previously.

Anyway, that's what I'm planning to be up to... 

What are you guys doing?

Wednesday, 16 April 2014

Write your own Virtual CPU in C++ (4001 CPU)

I've previously covered writing my own virtual CPU, but I never really fleshed it out and it was never a real thing... For a while, well over a year, I've thought about returning to that same topic... Today, this is the first post in that return.

The 4001 Processor... I'm going to borrow here from a lecture I watched by the excellently entertaining Dr Richard Buckland of the University of New South Wales, check him out on YouTube if you want, in one of his lectures he discusses a simple processor within his lecture, this first processor he creates on the board is the same processor, or very similar, I'm going to work us through here in this post... Richard called it the 4001...

If you want to know how to code this up and built it on Linux, then here's a quick intro to start the series of videos.

This first processor is very simplistic, it can do just one logical thing... Addition, it can add two numbers together, multiplication it can do, because multiplying is just repeatedly adding... It can subtract, because subtraction is just adding a negative number... And you can divide by repeatedly subtracting... I know this because I once got told off for designing code with my 486SX2 processor - which could only add and subtract in hardware - in mind, and so it was very inefficient on later Pentium chips... It really got me into trouble, but that's a story for another day.

So, we can add, what do we require from our functional chip in order to add?

Well, we need to have two registers in the chip, the register being where the two sides of our addition are placed... What else... Well, we need a way to load a value into these to registers.

So, in this most basic processor we're not looking to the future, we will add two separate instructions to load a value into each register.  Later we will discuss changing this.

When we've got a result back into one of our registers we need to be able to place our result somewhere else, so we need to save from one or both registers?  Well, for the sake of balancing things I'll say we'll save from both registers.

I also want to know the values we've just saved, so I need a way to print from our storage area... We'll therefore need an instruction to print out the value of a given location...

And I've mentioned the "storage area", this is the RAM the random access memory, which is the pool of places we can pull values into our registers from and push values back to, so we'll say this area is needed... but is it part of our processor?... No, but the processor needs to know where it is, it needs to know a base address to look for them, and so when we create our code we'll need to create an area of memory and tell the processor code where that memory is.

Finally, I want the processor to be able to beep... armed with this information we can draw our processor internal structure...



Of course two registers sat in a box don't constitute a processor, so we need to add some other parts... We need another register which tells us how far through our program we have gotten, this is the "Program Counter", and not only is it a register but it has a known behaviour, "When we power on the chip is it zero"... So it points at location zero in our memory.  And it has an active behaviour, the program counter reads the current position (or instruction) it can be incremented by one.

 

Armed with a program counter our processor can now start running through the instructions in a program...

And this is fine, technically we could run a program adding numbers through this now... But what about the numbers, the things we're storing, well each box in the memory grid we've already shown is a byte, that is 8 binary bits... 0 0 0 0 0 0 0 0... Each of these bits is given a value, known as its weight... Increasing from the right to the left.  The right most is known as the least significant bit, the right most the most significant bit.


Here we can see a single byte, the top green numbers are the position from left to right of the bits in the byte, and the red is the value we assign to each bit... Each bit then acts like a switch, if we flip the right hand most bit...

00000001

Then we look at the values, we have no values until the right hand position, its value is 1, so our number is now 1...

If we flip the left hand most switch as well...

10000001

We have the value 128 and the value 1 turned on... 128 + 1 = 129... So our binary byte now holds the value 129...

If we flip all the switches on...

11111111

We have 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1... which equals 255, so our binary byte holds 255...

And hopefully you've just spotted the problem our processor faces... If we exceed the maximum value a byte can hold, so we add two bytes which are 255 and 1... What happens?  A byte can't hold this value...

Well what happens in most hardware systems is that the binary rolls over, like an odometer in a car does as it counts up the miles, it will eventually get to the maximum and roll over to zero again... our byte does the same thing, from 11111111 all our bits get 1 added, and so they switch over to 00000000 and our mathematical add operation was not successful, because 255 + 1 does not equal the value we have of zero.

So how do we know this happened in our processor?  Well we need some indication, a flag, to be waved by our processor which says "I just overflowed", what's what this action is called, its called overflowing, we exceeded the number of bits we could hold and over flowed.


Is there anything else we need?  Well, we already said subtraction is just adding a negative number, so how do we represent a negative number?

Well, having just told you about binary we need to change it... Sorry...


We've changed the value assigned to the left most, or highest, bit.. It now represents minus 128, what does this do to our binary?

00000001

This is nothing nothing nothing... until the right most bit, which has a value of 1... So this still represents 1....

How about...

01010101

Well lets follow... 64 + 16 + 4 + 1 = 85

How about...

10000001

Well this is -128 + 1 = -127, the left most bit, the highest bit, being set on totally negates the whole value... Lets look at a higher number...

11010101

This is the 85 pattern, but with the left most bit... so its -128 + 64 + 16 + 4 + 1 = -43

From 85 to minus 43, just through one bit switching, that's a powerful change... So there must be a draw back?

Of course there is, when using a negative number in this fashion is uses up the highest value bit we have... So now we can't hold as high a value in the positive, because the numbers are used up... This limits our processor, so how about we invent a new instruction which switches into and out of, or toggles, between treating the registers as signed and unsigned?  So an instruction, to change a mode in the processor, how do we hold the status of this mode?  Yes we need another flag, and we need to say this flag is initialised as zero, or off, so by default our processor will start with unsigned number processing, or positive numbers only.


So when we're working with our processor later we need to test this flag out by giving it a high unsigned value and then toggling the flag and reading back the value, we should suddenly see that high signed value become a very negative number.

That is a lot of text, and I'm sure we want to dash off to create some code to represent this simple processor... But is there anything else we've not thought about?

Well, we considered overflow, what about the opposite?  What about if we subtract a number from a value which results in a number less than zero?  Well, we have our signing status flag so we can say our  processor automatically switches into using signs, so activates using negatives, and it can write out a possible negative number... But this isn't enough... what if the number being subtracted was so large that it will be a lower negative result than the lowest negative number we can hold?

The lowest negative number we can hold is just the negative bit set on and all the other bits set off...

10000000

So this is -128 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = -128... How do we store -129?

Well, we don't, what we get is an underflow.  Like an overflow, an underflow means the value is smaller than we can store, so we can't store it, we can't trust the result we see in the register... So what we need do is set another flag explaining to the world outside the processor that it could not handle the subtraction... You guessed it, we add an underflow flag...


Is this enough to handle our very simple processor?  Yes, I think we're there, at least for now.

There is an awful lot of computer theory introduced here, and I can only apologise at how poorly I'm introducing it to you, trust me though this is better than I was introduced to most of these ideas way back in ye olden days.

Right, Code time...

The Memory Class
Since we're writing C++ we'll put our memory into a class, what do we need?... Well we need an area of memory (bytes) we can access... As need to access them to read the values, and write new values into the bytes... it maybe nice to also clear the memory... And know how big the memory is...

Lets dive in...


The code will be at the bottom, complete...

And the g++ compile command used is:

g++ -std=c++11 Memory.cpp main.cpp -o cpu.o

What this tells us is we're calling the G++ compiler (the GNU compiler for C++) and we want it to conform to the latest C++ language standard, and the first file we're passing is the memory class, and then we're passing the second file; the main file; which uses the memory, we have to pass them in this order because we want to compile the Memory and then use it.  We can't compile the main which uses the Memory class without knowing what that class is can we?... No.  And then the final item is to output the binary as "cpu.o".

The CPU Class
Now we want to define our CPU class...


The new g++ compile command used is:

g++ -std=c++11 Memory.cpp CPU.cpp main.cpp -o cpu.o

As before this builds out system, but now the CPU has to use the memory and is used by the main, so its code is built after the memory but before the main program.

Now we have our basic CPU, what we'd want to do is call "Run" so the CPU we've written starts to run through its program... But we've not defined the behaviour we want... What will our virtual CPU do?

Well, we said it would run sequentially through a series of instructions... So we're is it going to get these instructions from?

There are two options here, we could put them into some storage, some memory, on the CPU itself... This would be called a cache, in truth on real processors this is called the L1 and L2 cache inside the chip itself.  But that does need somewhere else to have pulled the instructions from in the first place... So it itself read from general memory before the instructions in the cache could be executed.

The other option therefore is just to put the instructions in the main memory, well we have a memory class.  And putting the instructions into it lets us leave our CPU definition at Revision 0.5 and not rework our code... So, what we can do is say add the instructions in order from memory address zero upwards.

Now we know where the instructions come from we can say that the Program Counter is correct we initialised it in our code at position zero, and we reset it to zero!  Historically CPU's could pick anywhere to start from, some used to start around the 1024 mark, so they would skip over the first memory chip (which were usually 1024 bytes in size) because the first memory chip was the first powered up and most often was the first memory chip to go bad!  That's an off shoot, but it is something real engineers had to think about, and many of these little quirks are still hanging over us today.

Anyway, we've said our instructions are in memory and start at position zero in memory, what are these instructions?  Well, we're going to have actual instructions for the machine these are called "Operation Codes" or "op codes" and we're going to have data... You've already had a hint about this from the Memory class and its Write function, which had an address and a value.

Op Codes
Operation Codes are the heating heart of the processor, so we can start to define ours...

When the CPU reads an instruction of value zero from the Program Counter position.. It does nothing... zero... Null... Nothing... Nada... It does not even move to the next program counter location, it just stops... Zero is what we call the HALT instruction, we can halt our CPU.

What does this do to our Code?


Fetch & Decode
The run function gets a loop, moving through the instructions in memory, until there's a zero... That's fine, but its not really what a CPU does, a real processor actually does two things, firstly it fetches the instruction "Fetch" is the keyword here... That's because as we've already mentioned it might be fetching from a local cache of instructions, or having to move a chunk of instructions from the main memory into the local cache, or just executing as we are straight from the memory... "Fetch" can handle all this for us later on, so we can put that function in now and only make it fetch instructions from the main memory, but later we can expand "Fetch" without affecting our code.

But Fetch alone is not enough, once we have an instruction we need to decode its meaning... so "Decode" comes into play, this function puts some meaning in to the instruction for us... Now you might be watching this blog on your PC at home, if you are you're most likely running an Intel or an AMD brand processor, your processor executes in a set of op codes for those kinds of CPU... Your Apple or Android phone most likely uses a processor from a company using the ARM type chip, the ARM instructions are different to those on the Intel/AMD platform.  But the instructions are still bytes (numbers) underneath, but they mean different things because when the different brand processors come to "Decode" the number, they can mean different things...

In our CPU we've got one number instruction... Zero, which means HALT.  So we can write our Fetch and our Decode functions and make HALT do something.


We've invented our two functions, and we've changed the Run loop, it itself just keeps going and going... This is because we want to HALT the processor, but what do we mean by HALT?

Halting the Processor
So when we halt what we want is to stop exactly as it is, the running loop should stop, but the memory outside the CPU should stay unchanged, and the registers and status flags should not change inside the CPU.  In fact the ONLY thing we want to change is a new flag... A new flag which says "I'm halted" to the outside world...

This would let the system monitor the processor, think about your multi-core machines today, in laptops and mobiles you can have lots and lots of processor cores, but when not being used they can be put to sleep, we want our halt to work a little like that, except we're not halting to save power, we're halting to allow us to inspect the memory and status of the processor...


What does this do to our code?


Well, first of all you'll see we fix our access to the memory, to "Read" - my mistake - and then what we do is add the new Halt flag, default it to false when we construct the CPU and then we change the run function to fetch the next instruction into the decode function...

There are a couple of problems here however, firstly we're fetching directly into the decode function...second, the fetch function is not just fetching its incrementing the program counter for us, perhaps incrementing the program counter should be done with some logic so that we could alter how we move to the next op code later... What does this mean?  Because we're loading the fetch directly into the decode we don't have any way to inspect the current op code, we need some intermediary...

How about we fetch into one of the two registers in our CPU class?


So now we could halt and know the op code?... Right?... RIGHT?.... Nope.

Improving Fetch
We need to think a head to what other codes we'll have, well one of them is "Add"... Add takes two inputs, two numbers, to add them together... We only have two registers so as we start to execute an Add we'll put one of the two numbers we're adding into register 0 and so overwrite the op code we just loaded... If the Add overflowed and the CPU halted, when we looked back the status would tell us the last op code is not add, it will be the last value put into register 0 by the add... This is called register stomping.

In our very simple system we really don't want to add more and more registers to the CPU... And back in the olden days adding registers to CPUs was very costly, adding a whole register just to hold the last op code could make the chip cost prohibitively more to produce...

So, in our system here how are we going to get around this problem?  We don't want to add another register... We need some other storage... We only have one other place data can be, the Memory... Now, I've already said some processors used to skip over the first kilobyte of memory to start execution, well it seems if we want to use a piece of memory as the "last op code" then we need to make our programs start from a different location in memory...

Why make the program move?  Why not just put the op code at the end?  Well, we don't know how long a program will be, nor do we know how much data the program might want to handle, it might fill all the memory in the Memory class... So if we say we want to reserve the first byte of memory for the last op code, then we know that address zero is reserved, and not for use by the program instructions or data, and we say that user provided program instructions can't access memory address zero... This is a new rule... we can code this into our CPU construction & reset functions, and later when we come to add the other op codes enforce this rule.


Yes, now we could halt our CPU class and query the Memory class and know the op code which caused the problem, this is the first step towards allow us to Debug our CPU code.

More Op Codes
What other Op codes do we need?  Well the next is to Load a value into Register 0... "LOAD0", and it has a following byte which is the value... so this op code when in memory has its code value and is followed by the actual value to load... We'll give it the op code 1, so in our memory it we were going to load 67, we would find the byte 1 and then the byte 67.  This makes up the instruction "LOAD0 67".

That was easy, how about another, Load a value into Register 1?... "Load1"... we'll give it the op code 2... and again it is followed by a value.

And then the op code for Add... How about 3... And this puts a result somewhere, well we'll put that result into Register 0.


The loading functions are fine, we're moving a byte from memory storage to register storage, they're the same size because we know the architecture of our processor and code... So we don't need any checking...?  Well, what if we're at byte 255, the highest possible byte in memory, and we see either load instruction?  The whole emulated CPU would crash, because we're reading beyond the range of the memory.

Memory Address Range
The load function incrementing the program counter to pass by the data, or even trying to read data which is already beyond the range, or the fetch instruction trying to increment the program counter beyond the end of memory is bad, it violates the whole idea of the memory space.  So where in our CPU code so we handle this?  Well, a real processor handles this by limiting the area the processor can address from user operation codes.  You've seen this already with the new reserved address to skip the byte we're using as a temporary storage of the op code.  This reserved address is the lower bounds of any data, be that an op code location (pointed to by the program counter) or values following an op code.  And the upper bound is the max-address...

How might we want to handle this?  Do we want to limit the program counter in the Fetch function to be less than the maximum address minus the longest instruction length?  So our longest instruction is Load0 and Load1, they are one byte plus one byte of data... the maximum address in Memory is 255... so 255 - 2 is 253, so we limit the Program counter to always be below 253?

The answer is possibly, we're making this up as we go along after all, what cons are there to that approach?  Well, we loose 2 possible bytes from the program if the following instructions are a single byte... And we're still not going to run the program correctly, because we need to halt... a program must end and the CPU stop... to the last instruction has to be zero?...

Do we assume that the program counter going out of memory range is automatically a HALT?

You can try other options in your code, I'm however going to assume an out of range is a HALT... But what will be my range?... I'm going to avoid complex "checking" of the length of the operation, known as look-a-head, and I'm going to limit the program counter to never increment beyond the maximum address less the longest instruction length, and I'm going to call this the address ceiling.  Lets jump to our code.


There are a couple of fixes in the code - to reset - and we split the Add and Halt functionality into functions of their own, so we may call them from different places.  But mainly we're adding the new address ceiling and capping the Fetch to call Halt upon reaching the cap...

Other Instructions
The other instructions we wanted were to beep... Well, we'll just add a single op code 4 for that... 

What about saving the result of an addition back to the memory?  Well, this is more complex than previous operations, out only operation is add, and the result is placed into register 0, so we want to store the value in register 0 to a memory address, but we can't directly operate on a value in the Memory.. First we must load the target address from the program in memory into Register 1... Then write from the memory the value of register 0 into the address held in register 1... We can not in this model processor store from register 1, because we need at least one other register to store the target address to.  We'll give this save operation the op code 5 and it will take a second byte the address.

And finally, we want to print a value from a memory location to screen... This is going to be a two byte instruction, the op code and an address in memory.  We'll give this last one the op code 6.

Importantly this last instruction can print any value from memory, an op code or data... Do we also give it access to the reserved address?  I would say Yes, because we want to debug the last op code if something goes wrong.  However, this function does not directly output a register, why is this?  Well, lets think about how we print from a memory location, can the Memory directly write to the screen?  In a real system no... First to write to the screen you have to load the memory location into a register, so this op code 7 printing function uses the register, it actually performs a load0 and then prints.  This instruction is another instruction which is more complex than previous instructions, because it contains another simpler operation we already do, a load.

Let us bring our code up to this level before we think further...


Inputting a Program
I mentioned in a previous blog entry I had been watching lots of videos about the Altair 8800 computer, well we're going to pay our homage here, because we now need to put some instructions into memory and then run them...

On the Altair one does this by setting the location in memory to point to and then inserting a number into it.  We're going to do something similar, just starting from the base address of 1...

What will our program be?  How about One plus two and then save the value and print that stored value followed by a beep!  That's pretty much going to exercise our whole CPU and tell me whether to this point any of the code we've written actually works... Lets just pseudo code our program though...

Load0 1
Load1 2
Add
Store
Print
Beep
HALT

Where do we save to and hence print from?... We can see the program is quite short, so we could put the data at location 100 and know we're not going to trip over it...

But that puts some data in a strange location way out in memory, how about then we count the number of bytes we use and determine where we can safely put our value?

Load0 (1) plus data (1) = Total Bytes 2
Load0 (2) plus data (1) = Total Bytes 4
Add (3) = Total Bytes 5
Store plus data (?) = Total Bytes 5/6... So can we add our data at 6?...

Lets look at the code:

void CPU::Store()
{
// Load the target address into register 1
m_Register1 = m_TheMemory->Read(m_ProgramCounter);
++m_ProgramCounter; // Skip the memory location data
// Write the register 0 value to this address
m_TheMemory->Write(m_Register1, m_Register0);
// Remember the order of our parameters
// was ADDRESS then VALUE!
}

No, because after we write the program counter is not on the next byte, its actually pointing to that byte we just saved at... What then happens to our program?

The program would end up with the value 3 being stored in byte 6, which is then Fetched as the next op code, this results in register0 and 1 being added together again...

The net result is the program would do two adds, but the second add would do nothing of consequence except slow down our program moving onto the Print function... The program would still work... By sheer blind luck... Change the values of their of the two loads the it will go crazy, try it out later.

What do we call this?  Well, this is runtime corruption of our program.

Do we want our program to act in this way?  No.  Do we want to place the data somewhere randomly in memory?  No... What might be a good solution then?  Avoiding randomness and avoiding program corruption?

How about we add data to the first byte after the program instead of somewhere random?  Yeah, I think that's a good idea, so how big is the whole program?

2 Bytes
2 Bytes
1 Byte
2 Bytes
2 Bytes
1 Byte
1 Byte

That's a total of...11 bytes, so the first address for data is 12, after the end of our program.  This is important later!

Machine Code
Now armed with our op codes, what are the bytes we're going to enter into memory, our first machine code program, for our virtual CPU?

1 1
2 2
3
5 12
6 12
4
0

Adding this to the code then...


So we insert the program, and the first time we run it, we can check the bytes in memory, and see the output... 12...? 1 + 2 = 12... Nope...

What have we done wrong (alright, what have I done wrong?)... yes, I've done the print function wrong, it loads the next byte of instruction into register 1, then uses that as the address to load into register 0 and prints that to the screen.

And when its re-run it works fine and we see 3 shown...

This completes this large and heavy introduction... In the next posts we'll tackle the error or underflow and overflow we've set up flags for.  We'll do multiplication, subtraction and division, and we'll also update the main program to include a serial terminal to let us input programs dynamically - like you would on the front of the Altair...

Part 3 will be related to writing an assembler for our virtual CPU, so stick around.


Source Code
The full source codes to this point are published on pages with this blog here:

Memory Header
Memory Implementation
CPU Header
CPU Implementation
Main Program


Part 2 here.


Thanks to David for pointing out the above link was wrong, for years... Why did no-one else notice?!?!?!  And does this explain why there have been 10K views to part 1, and 1K views to part 2???!?!... Maybe :)