Commodore 64: cc65 and the User Port
Ok, we've build the user port to parallel port adapter. We've build the parallel port interface. We've hooked a shift register onto it and now we want to control it. Doing this through BASIC would be fun... but compiling real C code and getting this onto the C64 will be thoroughly more enjoyable. Go and download cc65, along with your favourite emulator. VICE works very well on Windows.
Getting a sample running
Before we even think of real hardware, let's get the samples running on VICE. Once you have everything installed, open a command prompt. Switch to the directory where cc65 is installed. Also set your path to the cc65/c64/bin folder. Run cc65 to check if you've got everything correct.
Microsoft Windows [Version 10.0.10586] (c) 2015 Microsoft Corporation. All rights reserved. C:\Users\windows_user_1>d: D:\>cd cc65 D:\cc65>set path=d:\cc65\bin D:\cc65>cc65 cc65: No input files D:\cc65>
Download the samples from the github repository and save them nearby. Switch into the samples folder. Choose a sample to compile; I chose ascii. Compile it. Don't expect much from the compiler if there is no error. We're going to use cl65 here which also does all the linking for us.
D:\cc65>cd samples D:\cc65\samples>dir Volume in drive D is Data Directory of D:\cc65\samples 26/05/2016 12:40 PM <DIR> . 26/05/2016 12:40 PM <DIR> .. 17/05/2016 02:26 PM 2,300 ascii.c 17/05/2016 02:26 PM 8,068 diodemo.c 17/05/2016 02:26 PM 2,455 enumdevdir.c 17/05/2016 02:26 PM 6,928 fire.c 17/05/2016 02:26 PM <DIR> geos 17/05/2016 02:26 PM 6,592 gunzip65.c 17/05/2016 02:26 PM 1,956 hello.c 17/05/2016 02:26 PM 3,772 Makefile 17/05/2016 02:26 PM 3,711 mandelbrot.c 17/05/2016 02:26 PM 7,345 mousetest.c 17/05/2016 02:26 PM 6,236 multidemo.c 17/05/2016 02:26 PM 69,766 nachtm.c 17/05/2016 02:26 PM 3,117 overlaydemo.c 17/05/2016 02:26 PM 8,573 plasma.c 17/05/2016 02:26 PM 5,865 README 17/05/2016 02:26 PM 2,876 sieve.c 17/05/2016 02:26 PM 5,269 tgidemo.c 17/05/2016 02:26 PM <DIR> tutorial 16 File(s) 144,829 bytes 4 Dir(s) 1,717,242,986,496 bytes free D:\cc65\samples>cl65 -O -t c64 ascii.c D:\cc65\samples>
Quickly check that there's a binary called 'ascii' in the folder with no extension.
D:\cc65\samples>dir Volume in drive D is Data Directory of D:\cc65\samples 26/05/2016 12:52 PM <DIR> . 26/05/2016 12:52 PM <DIR> .. 26/05/2016 12:52 PM 2,648 ascii 17/05/2016 02:26 PM 2,300 ascii.c 26/05/2016 12:52 PM 2,767 ascii.o ...
You've got a compiled application! Let's run it. Open up VICE (x64.exe is the c64 version) and choose File -> Autostart disk/tape image. You'll need to browse to where you compiled the sample and set the filter to all files.
Once you see 'ascii' (or whatever you compiled) double-click it.
Feel free to play with the other samples and see what C code is available and explained.
Poking the Port
BASIC had two commands that altered system memory. PEEK and POKE were essentially READ and WRITE. They allowed the user to change values in RAM or read values back. Hitting certain addresses did certain things. Specifically, on the C64, POKING 56579 altered the input/output configuration of the User Port and then POKING 56577 changed the values being sent.
To do this in C, we need equivalent functions. Fortunately, the cc65 wiki has all the information we need. It turns out that no function is required, you can simply write to ports by setting the address (via a pointer) to the value you require. To make it a little less difficult to read, they've also provided macros to do the same thing. They'll help when you come back to the code 6 months down the track and can't read a thing you wrote!
#define POKE(addr,val) (*(unsigned char*) (addr) = (val)) #define POKEW(addr,val) (*(unsigned*) (addr) = (val)) #define PEEK(addr) (*(unsigned char*) (addr)) #define PEEKW(addr) (*(unsigned*) (addr))
There's also pokes for 'WORDs' there, but we don't really need them. Look here for a huge list of what you can PEEK and POKE. Turns out there's more memory address to poke here.
Note: These defines are also in peekpoke.h, just include that!
Moving the cursor
By default, as per CMD or any other Console, the text just rolls down the screen. Scrolling (with a buffer) is something that you actually need to implement in C64, so either start preparing for a buffer, or just get ready to use a single screen and clean up after yourself.
I want to draw a diagram of the 8 LEDs I'm about to control, so for this purpose we'll need to be able to place characters at certain positions. This involves moving the cursor to the required location and then outputting the character.
Fortunately, functions are already there to do all this... just use the gotoxy as per the source listing below.
Controlling the parallel port and 74HC595
So, we now have everything we need to write out the data required to control the 595. I'll just list it below. There's more information back here on how it actually works.
#include <stdlib.h> #include <string.h> #include <conio.h> #include <joystick.h> #include <peekpoke.h> #define USERPORT_DATA 0xDD01 #define USERPORT_DDR 0xDD03 static const char Text [] = "Train Controller!"; void shiftout(int val) { int i = 0, zzz = 0; for (i = 7; i >= 0; i--) { POKE(USERPORT_DATA, (val & (1 << i)) == val ? 1 : 0); POKE(USERPORT_DATA , 2); for (zzz = 0; zzz < 1000; zzz++) {} POKE(USERPORT_DATA , 0); } POKE(USERPORT_DATA , 4); POKE(USERPORT_DATA , 0); } int main (void) { unsigned char XSize, YSize; int i = 0, z = 0, zz = 0; //set all pins to output. POKE(USERPORT_DDR, 255); /* Set screen colors, hide the cursor */ textcolor (COLOR_WHITE); bordercolor (COLOR_BLACK); bgcolor (COLOR_BLACK); cursor (0); /* Clear the screen, put cursor in upper left corner */ clrscr (); /* Ask for the screen size */ screensize (&XSize, &YSize); /* Top line */ cputc (CH_ULCORNER); chline (XSize - 2); cputc (CH_URCORNER); /* Vertical line, left side */ cvlinexy (0, 1, YSize - 2); /* Bottom line */ cputc (CH_LLCORNER); chline (XSize - 2); cputc (CH_LRCORNER); /* Vertical line, right side */ cvlinexy (XSize - 1, 1, YSize - 2); /* Write the greeting in the mid of the screen */ gotoxy ((XSize - strlen (Text)) / 2, YSize / 2); cprintf ("%s", Text); /* MARQUEE */ for (zz = 0; zz < 4; zz++) { for (i = 0; i < 8; i++) { shiftout(1 << i); for (z = 0; z < 8; z++) { gotoxy (((XSize - 15) / 2) + (z * 2), (YSize / 2) + 4); cputc((z == i ? 'X' : '_')); } } for (i = 7; i >= 0; i--) { shiftout(1 << i); for (z = 0; z < 8; z++) { gotoxy (((XSize - 15) / 2) + (z * 2), (YSize / 2) + 4); cputc((z == i ? 'X' : '_')); } } } /* Wait for the user to press a key */ (void) cgetc (); /* Clear the screen again */ clrscr (); /* Done */ return EXIT_SUCCESS; }
Note: some cputc calls reference defined characters such as CH_URCORNER, etc... These are PETSCII, the embedded character set of the Commodore. The wiki page has the numbers for the characters. Go to the include folder of cc65 and then cbm.h to see if the character is defined, otherwise just put the value in as a raw number.
And the result.. the bloody thing worked first go. Pretty scary actually... compiling and executing C code on the C64 was too easy. Of course, I cheated by using an SD2IEC. The load command was LOAD "0:TRAINCTL",8 followed by RUN.
You'll note that my keys are still on order... I can't wait for them to arrive as pressing 8 is tedious. Also that the last shot shows two Xs lit. Blame shutter speed and screen refresh times.
What's next?
Maybe it's time to hook up the serial port? nanoflite has provided a C driver which may well help us. Otherwise it's time to write a real railway controller for the parallel port interface... but I should actually finish that first.
SqlDateTime vs DateTime (The battle of the Milliseconds)
This was a good one. I had stored a data row both locally in SQL CE 4.0 and then remotely via a WCF service to SQL Express 10.50.4000. I also then stored the 'most recent' date of the most recent local row in a local 'settings' table as an nvarchar. This was formatted as yyyy-MM-dd HH:ii:ss.fff. I'd then pull this date from the settings table and send it to the remote service that asked are there any records newer than this date?.
The remote service would usually repond with the correct answer, when there were newer rows. But, every so often, it would also respond with Yes! In fact there is! Here is the record with the date you just specified!. WTH? I sent the remote service the exact date and it responds with the same record, which is supposedly newer (no greater-than-or-equals here) when it is exactly the same? ... or is it?
It should not have returned this record. The date should have matched the check date I sent the service and the is there a record with a date greater than the check date should not have been true...
DateTime objects stored in MSSQL lose accuracy
Turns out that, in the land of the datetime data format in MSSQL, that the accuracy is not maintained.
MSSQL, with the datetime format, can only store milliseconds of ##0, ##3, ##7. There's a lot of posts on this splattered all over the web.
Be careful when storing a datetime as a string
So what was my issue? I was getting the datetime from my local record and storing this as a string in another table, called settings, which was a key-value-paired one-size-fits-all user-settings table.
I was formatting it to the correct SQL format, preserving the milliseconds. Of course, I could, in this case, store any 3-digit millisecond value I required.
When I then read that back as a datetime in C#, the value was rehydrated correctly.
When I then sent that to the server, the value was up to 2 milliseconds less/greater than the ACTUAL record in my local db and the remote db. Therefore the server would, every so often, return the record I had based my date off... because I was storing it as text more accurately than MSSQL was in the datetime format.
SqlDateTime to the rescue
C# has a data type known as SqlDateTime. If you grab your DateTime from any object, pipe it into the constructor of a new SqlDateTime and then grab the .Value on the other end, you'll wind up with the DateTime as it would be stored in the database!
Tada! You'll no longer have to worry about inaccurate milliseconds!
In fact, you should probably switch ALL DateTimes in your C# app to SqlDateTimes if you want to get rid of shitty little bugs that will only occur when you do something at an exact millisecond that CANNOT BE STORED IN MSSQL.
End Rant.
net.tcp and IIS Express does not work
I've just spent time trying to rig up a test to get a working net.tcp server. Turns out that, in Visual Studio 2012 (and just about every other VS version), the IIS Express Server that one usually debugs through CANNOT support anything other than HTTPS and HTTP.
Q: Does IIS Express support non-HTTP protocols such as net.tcp or MSMQ?
A: No. IIS Express only supports HTTP and HTTPS as its protocol.
Please do not waste your time... When you hit debug on your net.tcp service, IIS Express will instantiate and you'll be thrown to your favourite web browser, staring at your root folder which is readable. You'll note that you'll also be on a port that has nothing to do with your web.config. This is because IIS Express has generated a random port and not cared one iota for your port configurations in web.config. You'll get nothing but connection refused when you try to connect to net.tcp://localhost:PORT/Service.
I was about to whinge that this tutorial on configuring a net.tcp service on IIS7 doesn't mention anything about incompatibilities with IIS Express. I suppose that one must assume by the header that the article is only for IIS7. A footnote would still be greatly appreciated for those trying to develop their code before deploying it!
If you ever want to get this to work, deploy and test your server on a real IIS instance and run from there... IIS7 preferably.
Note, if you need to debug... bring the server up, hit it with a simple query to make sure the app pool is active, and use Debug -> Attach to Process from Visual Studio. You'll find w3wp.exe way down the list with your Website's 'name' next to it. Highlight it and hit Attach to debug pesky server-side problems.
Once debugging in Visual Studio, you'll then need to open your source files (they can be in any folder, VS initially wont find them automatically) to set breakpoints. Note that exceptions will be caught in VS whilst debugging and will halt execution on IIS for all users. I have, once or twice, accidentally left the debugger attached overnight and had very angry clients in the morning with stalled connections.
FileSystemWatcher isn’t watching your files?
Quick note that just stole 2 hours of my weekend; of which I'll never get back... It seems that the FileSystemWatcher in C# it capable of watching files... yes.... but it turns out that the filters aren't as simple as you'd think.
Say you want to watch for new or changed text files in a folder? Filter = "*.txt" right? And the folder has a crapload of *.txt files? that's how it displays in Explorer? Right?
Wrong... Check out this sample code... yes, I'm holding the files open in my own source, but this is just a dirty way to show what's happening:
static void Main(string[] args) { string folder = @"d:\temp\fileWatcherFolder\"; FileSystemWatcher fsw = new FileSystemWatcher(folder, "*.txt"); fsw.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.LastAccess; fsw.Changed += fsw_Changed; fsw.EnableRaisingEvents = true; using (StreamWriter file = new StreamWriter(folder + "test5.txt")) { Console.WriteLine("["); for (int i = 0; i < 10; i++) { file.WriteLine("test line " + i); Console.WriteLine("."); Thread.Sleep(1000); } Console.WriteLine("]"); } Console.ReadKey(); } static void fsw_Changed(object sender, FileSystemEventArgs e) { Console.WriteLine(e.Name + " was changed!"); }
From above, you can see that I start a watcher on a known folder, I then start a new file and write to it. The output should always show that updates are occurring. The updates never come... and this is symptomatic of what I had with another external application that was holding files open. It also then presented a bigger issue of adding tildes to the end of the extensions.
It seems that this is an issue in .NET regarding file flushing. The application with the file handles open must flush the data out for a FileSystemWatcher to pick it up. Depending on the speed and quantity of files being flushed, the watcher may have to be customised to cope with the load. Check out the InternalBufferSize parameter.
Note in the above code, you can force a file.Flush() after the file.WriteLine, you'll get a single event. If you use file.FlushAsync() then you'll get ALL of the events! There is no threading in my example code, so the fact that async works makes sense. Of course, if you have no control over the application that is writing the files (as I didn't) then you're stuck trying your hardest to watch the files.
I still can't see my files?!
It gets better though... on some versions of windows, you may will not even see the filename with the expected extension... there may actually be a tilde at the end of the name... invisible to the naked eye. Explorer will show "test5.txt" but the watcher will see "test5.txt~"! It seems that if another application has the file open, the operating system keeps the filename with a tilde at the end until 'flushed' to disk. I haven't been able to reproduce this via the source above, using notepad to hold files open... but I know it happens. The application I had to deal with spewed files out quickly, holding them open, and the watcher only saw "txt~" extensions...
If this is the case, then you can loosen your filter to "*.txt*". Note that if you have a LOT of files being created, then this will slow down the watcher considerably. I actually recommend that you set up a second watcher, one for "*.txt" and the other for "*.txt~".
It's only when you watch the files with a filter of *.* that you'll see the crappy extension names. So when trying to debug this... set your filter as 'relaxed' as possible to capture changes and tighten it when you know what you are looking for.
Be careful! It seems others have had the same problems: FileSystemWatcher class does not fire Change events when NotifyFilters.Size is used.
A quick note on interfacing with JSON services via C#
I'm sure there are 100s of ways to manually create classes for JSON objects and then decipher them upon web-service response, but I've just stumbled across a fantastic site called 'json 2 csharp' that creates the classes for you. Just slap in your response (try to get a fully-fleshed out one with as fewer nulls as possible) and it'll generate the class structure.
You can then use the NewtonSoft JsonConvert deserialiser to populate it.
An example
Here's a link: jsontest 'date' example. It produces the following response:
{ "time": "05:13:02 AM", "milliseconds_since_epoch": 1425532382121, "date": "03-05-2015" }
From here, you just copy the entire response and paste it into the text field on the json2csharp site.
Hit 'Generate' and the site will spit out the following:
public class RootObject //rename this! { public string time { get; set; } public long milliseconds_since_epoch { get; set; } public string date { get; set; } }
Note that 'RootObject' is a little boring... rename it to 'DateResponse'
Add a helper library to your code to easily pull JSON responses (and POST):
public static class JSONUtilities { public static string GetJSON(string url) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); try { WebResponse response = request.GetResponse(); using (Stream responseStream = response.GetResponseStream()) { StreamReader reader = new StreamReader(responseStream, Encoding.UTF8); return reader.ReadToEnd(); } } catch (WebException ex) { WebResponse errorResponse = ex.Response; using (Stream responseStream = errorResponse.GetResponseStream()) { StreamReader reader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")); Console.WriteLine(reader.ReadToEnd()); } throw; } } public static Tuple<HttpStatusCode, String> PostJSON(string url, string jsonContent) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Method = "POST"; System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); Byte[] byteArray = encoding.GetBytes(jsonContent); request.ContentLength = byteArray.Length; request.ContentType = @"application/json"; using (Stream dataStream = request.GetRequestStream()) { dataStream.Write(byteArray, 0, byteArray.Length); } long length = 0; try { using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { length = response.ContentLength; using (var reader = new System.IO.StreamReader(response.GetResponseStream(), encoding)) { return new Tuple<HttpStatusCode, string>(response.StatusCode, reader.ReadToEnd()); } } } catch (WebException ex) { // Log exception and throw as for GET example above Console.WriteLine("ERROR: " + ex.Message); throw ex; } } }
And now you can bring it all together:
private bool Get() { var result = JSONUtilities.GetJSON("http://date.jsontest.com/"); var dateResponse = JsonConvert.DeserializeObject<DateResponse>(result); Console.WriteLine("Got Response: " + dateResponse.date + " [" + dateResponse.time + "]"); }
Too easy!