Friday, 22 November 2024

A Commodore 64 Emulator in Flutter: Part 2

Foreword

In the previous post I gave introduction to my idea of creating a C64 Emulator in Flutter.

I explained briefly how to install Flutter and configuring an IDE for using it and looked at a Hello World project created by IntelliJ.

In this post we will continue our journey on creating a C64 emulator in Flutter.

Introduction to using BloCs

Let us look a bit more into using BloCs. From the previous post we know that BloC stands for Business Logic component.

The term describes it all, which is splitting your business logic into a component. This imply having a stateless widget. The state will be kept in the BloC component, and when state changes, it will send the changed state to the widget, so that the widget will be re-rendered with the new state.

When using BloCs you basically need to remember two classes: BlocProvider and BlocBuilder. With BlocProvider, you create an instance of your BloC. BlocProvider then allows you to inject this instance further down in widget your tree by means of a BlocBuilder.

To use these classes in your widget tree, you should first create a proper BloC class, which I will discuss in the next section

BloC, Event and State

Let us create BloC class. Two things a Bloc class needs is an Event class and a State class.

You might remember from the previous post, I was illustrating by means of a diagram explaining what the purpose of an Event and a State were.

An Event you would use, for instance, when you click a button in your flutter app and you want your BloC to react in a certain way. You would then trigger an Event when you click a button, and your BloC will listen for that event.

In response to the event, your BloC will do some processing and afterwards it might have some data you want to display in your widget. In this case, your Widget will emit State, which your BlocBuilder will use to construct a widget for display.

Let us start by creating an Event class. Our Event class will be extending Equatable, for which we need to add the following dependency in pubspec.yaml:

equatable: ^2.0.5
pubspec.yaml is like a project file in Flutter hosting different settings and dependencies. I will not be going into details of the pubspec.yaml here. There is quite a few websites going into depth about this file. However, I will mention if there is things that needs to be added to this file for our project.

With this dependency added, and Pub get being run, we can now create our State class:

import 'package:equatable/equatable.dart';

class C64State extends Equatable {
  @override
  // TODO: implement props
  List<Object?> get props => throw UnimplementedError();
}
Similarly we create an event class:

import 'package:equatable/equatable.dart';

class C64Event extends Equatable {
  @override
  // TODO: implement props
  List<Object?> get props => throw UnimplementedError();
  
}
We are now ready to create our BloC, but first we need add the following dependency:

flutter_bloc: ^8.1.3
Now we can create our BloC class:

import 'package:flutter_bloc/flutter_bloc.dart';

import 'c64_event.dart';
import 'c64_state.dart';

class C64Bloc extends Bloc<C64Event, C64State> {
  C64Bloc(): super(C64State());

}
Our created classes looks fairly empty, but it will get more body as go along. One thing you will notice with our C64Bloc class is that we pass an instance of C64State() to our super. This is our initial state and will force a render of our Stateless widget. There will be subsequent state change for which we will use emit() to update our UI. More on this later.

Let us now move onto using the BlocProvider class. Let us refresh ourselves on how our app class looks again:

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo 2',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

We will add the BlocProvider instance in the home: selector:

      home: BlocProvider(
        create: (context) => C64Bloc(),
        child: const MyHomePage(title: 'Flutter Demo Home Page'),
      )
So, in the create selector we create an instance of our Bloc which will be passed down our widget tree.

Th original instance of MyHomePage now moved down to the home selector.

Now somewhere within our MyHomePage class, we need to inject the created BloC via BlocBuilder.

However, let do first things first. IntelliJ previously created our MyHomePage as a stateful widget, but we want a stateless widget. This is easy enough and we change our class to extend from StatelessWidget instead of StatefulWidget.

Having made the change, The IDE complains that MyHomeClass now needs a build method. So, we let the IDE implement this method. With all this our MyHomePage class implifies to the following:

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    throw UnimplementedError();
  }
}
The IDE has created a TODO for us.  Let us implement it straight by making use of a BlockBuilder:

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<C64Bloc, C64State>(
      builder: (BuildContext context, state) {
        return Text('Hi There!!');
      },
    );
  }
}
And everything renders in the browser:
Our text is weirdly formatted in red and underlined because we did not surround it with a proper widget for applying styling. This will be addressed at a later stage.

As mentioned earlier when we create an instance of C64Bloc, an initial state is passed to its Super method. It is this initial state that triggers the drawing of the text when the app starts up. 

It would actually be nice to show something of our initial state in our widget, just to get a feel that everything is working end to end.

For this purpose, let us add an epoch to our C64State class:

class C64State extends Equatable {
  final time = DateTime.now().millisecondsSinceEpoch;
...
}
We can now adjust our MyHomePage class as follows:

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<C64Bloc, C64State>(
      builder: (BuildContext context, state) {
        return Text(state.time.toString());
      },
    );
  }
}

And the rendered page look like this:

Our Epoch gets displayed!!

Using Events

So, in the previous section we basically just shown a timestamp once off, when the app started up. But what if we wanted to update the timestamp on the screen when you press a button?

This is where events come in. When you click a button, an event will be fired which our BloC class will listen for and update the state.

Before we can do this, we need to wrap our BlocBuilder into a more meaningful container, which will allow us to easily add a button. For this we wrap our BlocBuilder into a Scaffold, which will move our BlocBuilder to the body attribute:

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('BLoC Example')),
      body: BlocBuilder<C64Bloc, C64State>(
        builder: (BuildContext context, state) {
          return Text(state.time.toString());
        },
      ),
        floatingActionButton: FloatingActionButton(
          tooltip: 'Step',
          onPressed: () {  },
          child: const Icon(Icons.arrow_forward),
        )
    );
  }

With all this we get an appBar and button on the lower right of the screen:


Now, we need to fire an event when the button is pressed:

...        
        onPressed: () {
          context.read<C64Bloc>().add(C64Event());
        },
...
This is in effect the shorthand for getting our injected C64Bloc instance and adding an event to it when we press the button.

Now, with all our changes, lets do a test run. Upon startup we will see an initial timestamp. However, as we click the button, nothing happens. What is going on here?

Looking at the console, we see an exception is thrown every time, and looking closer, we see the error is thrown in our C64State class as highlighted here:

class C64State extends Equatable {
...
  @override
  // TODO: implement props
  List<Object?> get props => throw UnimplementedError();
}

Now this was part of the original code generated when I asked the IDE to implement the missing methods for me. Clearly, the IDE left me a TODO, which I ignored, but also purposefully ignored to illustrate a point 😁.

Lets unpack this a bit more. For starters, for what is the get props for? This is a method that need to be implemented when you extends Equatable.

Why do we need Equatable? This makes it easy to compare objects against its other. This is a common problem in many Object Oriented languages. How do we know if objects are equal? You can compare references, but that is a useless excercise since you will always different objects and it will not tell you if their values are equal.

Equatable comes to the rescue here, but you need to do something from your side. For get props you need to specify a list of property of your class that needs to be considered for comparison. In our case, we have just one property to add, time:

import 'package:equatable/equatable.dart';

class C64State extends Equatable {
  final int time = DateTime.now().millisecondsSinceEpoch;

  @override
  List<Object?> get props => [time];
}
If we now recompile, we see everything works as expected, and with any click, the timestamp updates.

I question might arise at this point: Why is a proper get props necessary for C64State, but not for C64Event? The answer is that it is always important for Flutter to know if a state has changed in order to trigger a redraw of your widget. This is where proper object comparison comes in.

In Summary

In post we continued our journey in creating a C64 emulator in Flutter. We explored basic concepts of Stateless widgets, state and events. These are all building blocks for working towards our emulator.

In the next post we will be implementing a couple of CPU instructions, and single step through a simple program, watching a snippet of memory and registers as we go along.

Until next time!

Saturday, 9 November 2024

A Commodore 64 Emulator in Flutter: Part 1

Foreword

It has been a quite a while since I last publish a post in my blog.

It was quite an eventful couple of years. I was fortunate to work fully remote during lock down and even a couple of years thereafter.

However, since last year they gradually asked us to come back to office more often until we found ourselves back at office full time. Suddenly I found myself wasting more time in traffic.

It was clear for me the whole concept of working from home is gone, so, with that in mind, I find a job closer to home with more opportunities.

One of the opportunities I got at my new job was to learn a new cross platform UI framework, called Flutter.

My colleagues at my job is quite a bit younger than me, and with that always comes an atmosphere of more enthusiasm. They mentioned that they read articles that you can even write games in Flutter. Something else that also picked my interest about Flutter is that you can also run it in a browser, but it doesn't make use of the DOM which HTML uses for rendering. Instead, it renders everything to a canvas and make use of WebGL as well as WebAssembly.

So, sounding like Flutter having a lot of power under the bonnet, I ask myself:"Why not write another Commodore C64 emulator in Flutter?" That is what I will be exploring in the next couple of blog posts.

In this post I will be covering some of the basics of Flutter. I coming series I will just build on the idea and add more functionality.

You maybe wondering what my plans are for my Amiga on an FPGA project. I have decided to put this project on hold for a bit, just to take break, and just try something new, like this Flutter project.

Hope you enjoy this series!

Installing Flutter

As mentioned, Flutter is a cross platform framework. You can create apps for Android, IOS, Windows, Linux and for web. In my series, however, I will mainly focus on web on a Linux machine.

The following link explains how to install flutter on your development machine:

https://docs.flutter.dev/get-started/install/linux/web

In short, you basically need to download the sdk, extract it to a path, and then add a location to your $PATH environment variable so that the you can run the flutter executable from the command line.

You obviously will also need to setup your IDE to develop in Flutter. In my case, I am using IntelliJ Ultimate and I just installed the Flutter Plugin, so I can easily run and debug Flutter applications.

Creating your first Flutter app

Let us create our first Flutter app using IntelliJ.

With IntelliJ open select File/New Project and Select Flutter from the generators:



Also, at the top ensure that you have selected the path to your Flutter SDK.

Now click next and specify a name and a path for you new project:


At this point, we should maybe just talk a bit about naming conventions. All file names are in lower case, with words separated by underscores.

With classes inside the files, you make use of Camel case for naming.

Now, if you click create, your new project will be created. Your project structure will something like the following:




In general, the only thing you need to worry about is the lib folder, where you put all your source code and pubspec.yaml, where you put important settings of your project.

If you look at the top, you will see a drop down displaying "no device selected". Click this drop down and select Chrome.

Your toolbar will now look like this:

Now, click the play button. You project will be build, and eventually a Chrome Browser window will open with your app:


Flutter have created a default app counting how many times you have pressed the plus button.

Unpacking the generated code

Let us unpack the generated code a bit. Looking at the code, we have everything generated in a single file called main.dart. You are not bound to this and you can put your stuff into multiple files.

First thing we see in main.dart, is a main method:

  void main() {
    runApp(const MyApp());
  }
We create an instance of MyApp and then run it.

Let us look at the definition of MyApp:

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

To save space, I have removed most of the comments.

Now, we see MyApp extends StatelessWidget. I will cover in a moment on what a StatelessWidget is, but for now just think of it as some kind of widget.

In Flutter, Everything you display on the screen is a Widget. One of the things a widget always have is a build method, which, you have guest it, returns a Widget.

MyApp is our main widget, which in this case is an instance of MaterialApp. MaterialApp does the heavy lifting, applying the appropriate styling to all visual components, to give your App the look and feel of a Material application.

Within the MaterialApp instance, there is a property home, where you specify your homepage widget. This is basically the entry page to your application. Once in your homepage, you can also navigate to other pages as well, almost like a web application.

Next, let us have a look at MyHomePage:

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

Firstly, look at the interesting way the constructor works. You will always specify the parameters in curly braces. Also, you use the word required, if it is compulsory to specify the given parameter.

At this point you will notice that MyApp is a StatelessWidget and MyHomePage is a StatefulWidget. These are two very important concepts in Flutter.

A stateless widget is immutable. In order to change what it displays, you need to destroy the instance and create a new one.

A statefull widget on the other hand, can store state, and if you need to change what it displays, you can keep the same instance.

Having said all this, our demo created app might sound confusing that we have a stateful widget inside a stateless widget. Does this mean that if our HomePage change that our Myapp instance will be destroyed and a new one created? Not at all. Remember, although MyHomePage lives inside Myapp, it is still self contained. So, it can do display updates within its own area on the screen, and without effecting the parent.

If there is actually display elements outside MyHomePage, inside the parent, it would not be possible to redraw any changes. To make changes to such elements, one would need to create a new instance of the Stateless widget, which in this case would mean bringing down the application, which we don't want 😁

Stateful widget are actually very smart, and usually just redraw parts of the screen that have changed, thus avoiding redrawing the whole screen. Having said that, despite the cleverness of Stateful Widgets, Flutter textbooks actually warns against using them and advise to rather use Stateless widgets where possible.

The reason authors provide for avoiding Stateful Widgets, is because code for managing the screen and business logic can easily get mixed up together. Separation of concerns is actually key here.

In this series of creating a Commodore C64 emulator in Flutter, I will take these authors advice to heart and stick with Stateless beans.

In the next section I will describe what my approach will be for the rest of the series.

The approach

Let us discuss the approach I am going to take for creating a C64 emulator in Flutter.

As mentioned earlier, I am going to stick with Stateless widgets. As the name imply the widget itself is not going to have state. So, one will need to keep the state outside the widget.

There are quite few patterns in flutter that can provide this functionality of providing state outside a stateless widget. I will be using the BLoC pattern, which is short for Business Logic Component.

Let us use our C64 Emulator as an example to explain the concept of a BLoC. The following diagram sums up everything:

It is still early days for our flutter Emulator, so one cannot expect any fancy stuff at his point 😂 At this point we will incrementally add more CPU instructions to our emulator, and single step through 6502 machine code, and seeing the values of the registers and a small section of memory.

Clicking on the Step button on the widget will trigger an event for which the BloC will listen for. Upon receiving the event, the Block will execute on CPU instruction which will potentially effect registers in the CPU and locations in memory.

When the BLoC has executed one CPU instruction, it will send a state update to the widget, which in effect will destroy the widget and create a new one with the updated values. The state will contain the values of the registers and a snippet of the updated memory. The widget in turn will display this to the user.

In Summary

In this post I gave an introduction to my idea of creating a C64 emulator in Flutter. I will start off using a Stateless Widget with a BLoC.

Initially my Emulator will just be single stepping through CPU instructions, showing the values of the registers and memory after each step. Once we have implemented all 6502 CPU instructions, we will move onto more interesting visual elements of a C64 emulator.

Until next time!