Ayman Bagabas

tech, code, misc.

About me
Blog
Blog index
Projects


© 2018 Ayman Bagabas

Writing a Chip-8 emulator

17 Sep 2018 0 Comments

If you ever played retro games on modern computers, then you probably know what an emulator is. Chip-8 is an interpreted programming language that was created originally by Joseph Weisbecker. Chip-8 programs get interpreted by a virtual machine. It offers a very simple monochrome graphics and uses a 4Kb of memory. It has the “8” part because all the system’s components, like CPU registers, have a size of 8 bits or 1 byte.

What is an Emulator?

The idea of an emulator is the same across any operating system. You basically try to translate the instructions of one system to another. The process of translation is done in three main parts:

  • Fetching opcodes
  • Decoding opcodes
  • Executing opcodes

These three steps happen in the CPU which is the main component of a system. The first step is to read operation codes from the program, then the CPU tries to decode these codes and then executes them accordingly.

Chip-8

Space Invaders

Before you get very excited, you really have to understand how the system works and behaves. Get familiar with your binary and hexadecimal conversions. A hex viewer may come handy when debugging a program.

CPU and Memory

Chip-8 is a simple system that has 16 CPU registers, each takes information up to 8 bits (1 byte). The program counter, I register, opcode placeholder, and a stack pointer, all have a size of 16 bits (2 bytes). Memory is a 4Kb memory where the first 512Kb is reserved for the interpreter which, this makes most programs written for the Chip-8 start at location 512. A minimal of 16 level stack pointer is required and it is used to store return locations from the program counter register.

Timers

There are two timers both countdown from 60 to 0. Delay timer is used for program events and its value can be set and read. And a sound timer which plays a beep whenever it reaches 0. After every operation execution, both timers get subtracted by 1.

Input

The Chip-8 uses 16 keys of input, (0x0-0xF) which are usually mapped to:

1 2 3 C        1 2 3 4
4 5 6 D   --\  Q W E R
7 8 9 E   --/  A S D F
A 0 B F        Z X C V

Usually ‘2’, ‘4’, ‘8’, and ‘6’ are used for directions.

Graphics

The Chip-8 has a 32x64 pixels monochrome display. It draws graphics on the screen using sprites. “A sprite is a group of bytes which are a binary representation of the desired picture.” And they may take up to 15 bytes. Chip-8 provides a set of predefined sprites for representing hexadecimal digits from 0 to F. For example:

  "1" |	Binary   | Hex
------+----------+-----
  *   | 00100000 | 0x20
 **   | 01100000 | 0x60
  *   | 00100000 | 0x20
  *   | 00100000 | 0x20
 ***  | 01110000 | 0x70

  "F" | Binary   | Hex
------+----------+-----
****  | 11110000 | 0xF0
*     | 10000000 | 0x80
****  | 11110000 | 0xF0
*     | 10000000 | 0x80
*     | 10000000 | 0x80

These sprites should be stored in the reserved interpreter area 0-512 of memory.

Implementation

Start with the big picture, how does your computer works? After you boot your computer it starts initializing components and devices. This includes inputs, outputs, graphics, CPU, etc. Then it loads the system and starts executing instructions sequentially. The main loop should look something like this:

#include // Chip-8 system
#include // Input
#include // Graphics

int main(int argc, char **argv) {
    initChip8();
    initInput();
    initGraphics();

    loadROM("INVADERS");

    while(systeIsRunning) {
        // Execute instruction
        executeOP();

        // Refresh display if flag is set
        if (drawFlag)
            drawGraphics();
        
        // Play beep if flag is set
        if (playSound)
            playBeep();

        // Set input and set keys states
        setInput();
    }
    return 0;
}

Chip-8 should implement ways to load and execute instructions:

void initChip8() {
    // initialize system
    // memory
    // registers
    // stack
    // graphics
}

void loadROM(file) {
    // read file into memory
    // starting from location 512Kb
    // or 0x200 in hex
}

void executeOP() {
    // fetch opcode
    opcode = (memory[pc] << 8) + memory[pc + 1];
    // decode opcode
    switch (opcode & 0xF000) {
        // execute opcode
        case 0x0:
            if (opcode == 0x00E0)
                clearDisplay();
            elseif (opcode == 0x00EE)
                // return from subroutine
        case 0x1:
        .
        .
        .
        case 0xF:
    }

    // timers
    if (delay_timer > 0)
        --delay_timer;
    if (sound_timer > 0) {
        if (sound_timer == 1)
            playSound();
        --sound_timer;
    }
}

Input and graphics depend on the libraries you will be using. I went with SDL2 for these two since it is cross-platform, very well known, and has a lot of documentation. You can refer to my Chip-8 implementation here.

What next?

Well, I really learned a lot from this project. Next, I want to extend this project to have a terminal based UI using ncurses. Or maybe work on a more complex system like the NES?

References


Leave a Comment



Arch Linux on Matebook X Pro

23 Jul 2018 0 Comments

Recently, I got a new laptop, Huawei Matebook X Pro. It has an i7 8th Gen. Intel CPU, 16Gib of RAM, 512Gib of SSD storage, Nvidia MX150 GPU with 2Gib of DRAM, and a beautiful HiDPI and touchscreen. It also comes with a fingerprint sensor. The first thing I did was installing Arch Linux, because well I am a Linux user! In the end, everything was working properly except:

  1. Two out of four speakers. See UPDATE1 down below
  2. The fingerprint sensor.
  3. Some keys in the Fn row.

The installation was very straightforward and like every other Arch Linux installation process. I followed their installation guide. After the installation is complete, everything was working properly except for some minor issues.

Installation

First, disable secure boot from the BIOS menu by pressing F2 while booting. Then have your Arch Linux installation media ready, then you can access the boot menu by holding F12 while booting. Once you boot into Arch Linux live boot image, you will notice that the font is too small, you can change that to use a large font with setfont latarcyrheb-sun32, or you could use any other font you like.

Multi-lib repositories

You probably want to enable multi-lib repos to support 32-bit software. Just uncomment that in /etc/pacman.conf or add the following:

[multilib]
Include = /etc/pacman.d/mirrorlist

Grub

I used Grub for the bootloader. Obviously, you want to use Grub for UEFI systems. For the ESP location, I had mine set to /boot/efi just to follow other Linux distors approach. Because of the HiDPI screen that comes with this laptop, Grub would very tiny to see, a quick fix is to set the GRUB_GFXMODE variable to something like 1600x1200x32. The available values can be fetched from Grub command line by executing videoinfo. Edit your /etc/default/grub file to include these lines:

GRUB_GFXMODE=1600x1200x32
GRUB_GFXPAYLOAD_LINUX=keep

If you are dual-booting you should install the package os-prober to make Grub detect Windows partitions. Don’t forget to regenerate grub.cfg using:

# grub-mkconfig -o /boot/grub/grub.cfg

After Installation

Because I have a dual-boot setup with Windows, I ran into a little problem after partitioning Windows partition. However, it was easily fixed using ntfs-3g package which includes the tool ntfsfix. After you complete the installation and boot into Arch, just run the tool with -b to fix bad sectors and -d to clear dirty flag:

# ntfsfix -b -d /dev/nvme0n1p3

In my case, my Windows partition was /dev/nvme0n1p3, you should change that based on your partition name. You can use lsblk or blkid to list all your partitions.

AUR helper

One of Arch Linux beauties is AUR, where you can easily get any Linux package installed with ease. I used the tool aurman which is IMO one of the safest ones out there. Here is the full list of AUR helpers. To install aurman:

$ git clone https://aur.archlinux.org/aurman.git
$ cd aurman
$ makepkg -si

Desktop Environment

Since this device comes with a touch-enabled and HiDPI screen, I decided to go with Gnome because it supports these two things pretty well.

# pacman -S gnome gnome-extra

This would automatically install Gnome with Wayland. Wayland doesn’t require any further configuration or drivers.

Nvidia driver & Bumblebee

The MBXP comes with Intel UHD Graphics 620 and NVIDIA Geforce MX150. If you are planning to use the Nvidia card for gaming, rendering, or anything your best two options are using Prime technology with Nouveau (open source NVIDIA driver), or use NVIDIA proprietary driver with Bumblebee. I decided to go with the later because it offers better performance. Install Bumblebee

# pacman -S bumblebee bbswitch nvidia mesa acpi_call lib32-virtualgl lib32-nvidia-utils

Enable Bumblebee service and add user to Bumblebee group:

# systemctl enable bumblebeed.service
# gpasswd -a $USER bumblebee

You probably need to Enable NVIDIA card during shutdown to avoid issues with using it. Now we need to tell Bumblebee to use bbswitch for card switching. Edit /etc/bumblebee/bumblebee.conf to include this:

[driver-nvidia]
PMMethod=bbswitch

Sometimes Bumblebee doesn’t detect the card which results in an error. You need to define the card BusID in /etc/bumblebee/xorg.conf.nvidia, just uncomment the line where it has “BusID” and set it to the actual device ID. You can get that using lspci. In my case, it was “PCI:01:00:0”.

Power Saving

I was able to get a great 8-10 hours of battery with low brightness. I am using TLP for managing power saving.

TLP

Install TLP:

# pacman -S tlp tlp-rdw

Enable TLP:

# systemctl enable tlp.service
# systemctl enable tlp-sleep.service
# systemctl mask systemd-rfkill.service
# systemctl mask systemd-rfkill.socket
# systemctl enable NetworkManager-dispatcher.service

And since TLP enables NetworkManager by default, there is no need to enable that. You want to set TLP default mode to the battery. Edit /etc/default/tlp to have these settings:

# Operation mode when no power supply can be detected: AC, BAT.
TLP_DEFAULT_MODE=BAT

# Operation mode select: 0=depend on power source, 1=always use TLP_DEFAULT_MODE
TLP_PERSISTENT_DEFAULT=1

Also since we are using Bumblebee with Nvidia you have to make sure that TLP doesn’t enable power management for the card which can break auto switching with Bumblebee. You should avoid using powertop --auto-tune since it enables power management resulting in breaking Bumblebee. Make sure you have these lines in /etc/default/tlp to exclude the Nvidia card from power management:

RUNTIME_PM_BLACKLIST="01:00.0"
RUNTIME_PM_DRIVER_BLACKLIST="amdgpu nouveau nvidia radeon"

Again make sure that you have the correct bus id for your card.

Audio

In /etc/modprobe.d/audio_powersave.conf add to enable audio power saving:

options snd_hda_intel power_save=1

WiFi

Since this laptop comes with Intel Wireless 8265, we can use iwlwifi power saving options. Add these options in /etc/modprobe.d/iwlwifi.conf:

options iwlwifi power_save=1 d0i3_disable=0 uapsd_disable=0
options iwldvm force_cam=0

Intel GPU

Power saving options for Intel GPU, just stick this line in /etc/modprobe.d/i915.conf.

options i915 enable_guc=3 enable_fbc=1

Suspend and Hibernate

Personally, I prefer hibernating my machine whenever I do not use it for a long time, that is why I use suspend-then-hibernate. Suspend to RAM works out of the box, but hibernate requires some work. First, make sure you have either a swap partition or swap file. I went with swap file just because it does not require partitioning. According to Redhat Recommended System Swap Space, 1.5 of system RAM is the recommended amount of swap for hibernation which is 24Gb in this case.

# fallocate -l 24G /swapfile
# chmod 600 /swapfile
# mkswap /swapfile
# swapon /swapfile

Now define that within /etc/fstab for auto mounting:

/swapfile none swap defaults 0 0

In order for hibernate to work, you have to define where the system should look for the resume image in your Linux partition. You can define that in the kernel parameter in your bootloader resume=UUID=ce6dd35a-08d5-4b49-a46c-eff1de8937ce. Here I am using partition UUID, you can get that with sudo blkid. Since I am using a swap file, I also have to define a resume_offset=645120 which is the location of the swap file in the partition. You can get that using sudo filefrag -v /swapfile. Finally, add resume hook after udev and i915 module in /etc/mkinitcpio.conf. Regenerate initramfs sudo mkinitcpio -P.

MISC

  • Add pcie_aspm=force kernel parameter to enable ASPM (Active State Power Management).
  • Disable watchdog, add blacklist iTCO_wdt to /etc/modprobe.d/nowatchdog.conf.
  • Take a look at Improving Performance and Power Management.
  • Regenerate initramfs sudo mkinitcpio -P.
  • Regenerate grub.cfg sudo grub-mkconfig -o /boot/grub/grub.cfg.
  • Install Plymouth.
  • Enable auto-brightness aurman -S iio-sensor-proxy.

Files

  • Config files used in this post etc.zip.

UPDATE 1 - fix sound

You can fix the sound issue with hdajackretask which is part of alsa-tools package then follow this picture and click on “Install boot override”:

hdajackretask

You might need to set “connectivity” to “internal” to get it working. Finally, recreate your initramfs sudo mkinitcpio -P and reboot.


Leave a Comment



Suspend then hibernate in systemd 239

18 Jul 2018 0 Comments

In systemd 239, they have added a new service that handles suspending then hibernating after a given amount of time. This is easier than using external scripts since it comes built-in with this version of systemd. You can check systemd version with systemctl --version.

First, you have to define the delay time before the system wakes up and go into hibernation and that should be defined in /etc/systemd/sleep.conf

[Sleep]
HibernateDelaySec=15min

Here, what we care about is the last line HibernateDelaySec where you can define delayed time. As you see, I have it set to 15 minutes after suspending.

Lastly, we need to override the default suspend to execute suspend-then-hibernate instead of regular suspend:

# ln -s /usr/lib/systemd/system/systemd-suspend-then-hibernate.service /etc/systemd/system/systemd-suspend.service

This will make systemd executes suspend-then-hibernate instead of suspend every time suspend is invoked.

Reference

systemd-sleep.conf

systemd-suspend-then-hibernate.service


Leave a Comment



Minesweeper using HTML5

13 May 2018 0 Comments

Obviously, everyone was born before the year 2000 knows the famous classic game ‘Minesweeper’. To me, I knew this game when I was little at the time where Windows XP was ruling everywhere. It is funny because at that time I did not know exactly how the game is played. That time the game was some kind of a luck game to me where you try to eliminate all the squares except the ones with mines until you either win or lose :laughing:.

Until recently, a friend pointed out that they saw a video of a guy who made a fully perfect AI to solve the game. I liked the idea so I decided to make one. I started by learning how the game works and it turned out to be very simple. There is a certain number of mines in the game and the player has to discover where these mines are to win the game. Every square has a weight that shows how many mines they are within the surrounding 8 squares of that particular square. If the weight was zero the game will reveal all the surrounding squares except the ones with the mines, otherwise, it will reveal the square itself. Although things did not go very well with the idea, I ended up making the game only.

How does it work?

With the help of HTML5 canvas element, you can draw geometric objects. We can achieve this dynamically using JavaScript. First, we create a canvas element in the page:

<canvas id="example" width="500" height="500">Any text here will get displayed if the browser does not support HTML5 canvas</canvas>

Then we use JavaScript to create objects and control their properties:

var canvas = document.getElementById("example");
var context = canvas.getContext("2d");
context.beginPath();
context.fillStyle = 'red';
context.fillRect(0, 0, 10, 10);
context.closePath();

This will create a square with side equals to 10 and a position of (0, 0). Easy and simple.

Minesweeper

Coming from an Object-Oriented Programming mentality, I wanted the ability to create classes just because I am used to it this way :smiley:. I was surprised when I knew that you can mimic creating classes in JavaScript .

I started by questioning what attributes each square has? And I came up with these: isMine, isFlagged, isDown, x, y, and weight. isMine tells if a square is a mine. isFlagged is when a square is being flagged or marked. isDown if a square is revealed. x and y hold the location in the game. weight is a number greater than zero where it holds how many mines within its surrounding squares. With these attributes I came with this JS class:

class square {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.isDown = false;
        this.isMine = false;
        this.isDown = false;
        this.weight = 0;
    }
    get x() { return this._x; }
    set x(value) { this._x = value; }
    get y() { return this._y; }
    set y(value) { this._y = value; }
    get weight() { return this._weight; }
    set weight(value) { this._weight = value; }
    get isDown() { return this._isDown; }
    set isDown(value) {
        this._isDown = value;
        if (this.isMine) {
            end = true;
        }
    }
    get isMine() { return this._isMine; }
    set isMine(value) { this._isMine = value; }
    get isFlagged() { return this._isFlagged; }
    set isFlagged(value) {
        this._isFlagged = value;
        if (value)
            numberofmines = (numberofmines <= 0) ? 0: numberofmines - 1;
        else
            numberofmines++;
        updateHeader();
    }
}

The game does not have a loop, it contains two main functions. The first one is called to initialize the game environment like setting mouse event handler. Here is where all the magic happens, whenever the user clicks the first click the timer starts and it passes the click to the game. If it was inside the game frame where the squares are located it will register a click for that square. Otherwise, it will start a new game. Which is the second function, the previous function is only called once. This one is called to clear all the previous data in the game and create a start a new game. Obviously, this is done with the help of other functions to make it a little more organized.

Source code

The source code is available at JSMinesweeper or you can try it out here .


Leave a Comment



Sync Google calendar using Vdirsyncer and Orage

08 Apr 2018 0 Comments

For a long time I wanted to have all my calendars and todo lists synchronized with my current desktop setup. Currently, I am using XFCE software, like Thunar, with Openbox to manage everyday stuff. Yes, I know with Gnome DE you can achieve that easily, but Gnome has a HUGE package dependencies. XFCE on the other hand is very light-weight and simple.

The other wonderful piece of software is vdirsyncer. It supports CalDAV protocol which is also supported by Google calendar. With simple configurations we can synchronize Google calendar locally. Then we can use Orage to view/modify our calendar files. Orage is a time-managing application that can manage your calendars, appointments, alarms, and todo lists.

First, make sure we have all the needed packages. Orage and Vdirsyncer should exist in most distros official repositories. In Archlinux, the installation can be done with sudo pacman -S vdirsyncer orage. It would be similar for other distros as well. Ubuntu/Debian sudo apt-get install vdirsyncer orage.

orage

Here you can see how orage highlights dates with events attached to them. For example, April 1st is Easter Sundy. That was pulled from my Google United States holidays calendar.

orage

Vdirsyncer setup

First, we start by configuring Vdirsyncer to sync Google calendars. My configuration file looks like this:

[general]
status_path = "~/.vdirsyncer/status/"

[pair calendar]
a = "google_calendar"
b = "local_calendar"
collections = ["from a", "from b"]
metadata = ["color"]

[storage local_calendar]
type = "singlefile"
path = "~/.calendars/%s.ics"

[storage google_calendar]
type = "google_calendar"
token_file = "~/.vdirsyncer/google_token"
client_id = "CLIENT_ID"
client_secret = "CLIENT_SECRET"

Here, we specified were the status for Vdirsyncer should be located. Mine is located under ~/.vdirsyncer/status. Then we tell Vdirsyncer to create a pair of calendars for bidirectional syncing, where a is the actual Google calendar, and b is the local files pulled from the cloud. Then we define the two calendars as storages.

  • storage local_calendar, here we specify what the type of this storage which is “singlefile” meaning Vdirsyncer would have a single file for each calendar that you have.
  • storage google_calendar, here where it gets interested. The type here is “google_calendar” which means use Google cloud to fetch the calendars. With this type you have to specify a token file, client id, and client secret. All of these are essential for synchronizing.

Now we have to enable CalDAV API for our Google account.

  1. Go here and create a new project. This is required so that you can enable CalDAV API for that project.
  2. Click on “Enable APIs and Services” or on the left side click on Library, then search for “CalDAV” and enable it.
  3. Now we need to get our credentials, client secret and id, for the API. Click on “create credentials” then choose “CalDAV API” for your API and select “Other” for Application type. Click next and choose a name e.g. “vdirsyncer” then click continue.
  4. You will get your client id, stick it into your configuration file.
  5. We still missing the client secret. Click done after you get the client id then click on the credentials name and get the client secret from there.

Now we have Vdirsyncer config setup, we need to authorize it to access our Google account.

$ vdirsyncer discover calendar

This will try to authorize the pair ‘calendar’ defined in the config file. A browser window or a link should pop up to complete the authorization and that would create the access token defined in the config. Now we can synchronize Google calendar by

$ vdirsyncer sync

Read more about Vdirsyncer.

Orage setup

Now we need to tell Orage where are the calendar ics files so that we can view them in Orage. Run Orage then go to File -> Exchange data, and add all the pulled ics files from ~/.calendar. Notice the read only check mark, if you want to modify your Google calendar you have to make sure to uncheck that when you add your calendar file which is the same as your Google account name “google@gmail.com.ics”. You should see all your calendars events highlighted within Orage. You can play with Orage settings to hide the window borders and tray icon. When double click on a date an events window should pop up then you can add events, todo, etc, but make sure you select your Google account file from the top bar in order to have it synchronized.

Using Cron to synchronize periodically

You can add a cron to automate the sync command. crontab -e

MAILTO=""
@hourly vdirsyncer sync

This will sync calendars every hour. Make sure you have a cron service running. I am using “cronie” for my setup and with Archlinux, I had to enable the cronie service for that to happen.

$ sudo systemctl enable cronie
$ sudo systemctl start cronie

Leave a Comment



Dropbox client on Ubuntu server 16.04

06 Aug 2017 0 Comments

Dropbox, in my opinion, is the best cloud service available. I wanted to have one shared dropbox folder that is accessible from all my virtual machines running on the server. I am using a ZFS drive as a storage drive, the dropbox folder is located in the ZFS drive. Dropbox service is running as a normal user, not the root, and the server is using systemd to start the service after booting the system.

First, you have to have the libxxf86vm library, in Ubuntu 16.04 you can install it using sudo apt install libxxf86vm1 then download Dropbox.

wget -O dropbox-x86_64.tar.gz https://www.dropbox.com/download?plat=lnx.x86_64

and if you are running a 32-bit system use:

wget -O dropbox-x86.tar.gz https://www.dropbox.com/download?plat=lnx.x86

Now choose where you want to put Dropbox binaries and extract the files, in my case I used /opt/dropbox

sudo mkdir -p /opt/dropbox
sudo tar xzfv dropbox-x86_64.tar.gz --strip 1 -C /opt/dropbox

and because we are using a /opt/dropbox we have to link it to ~/.dropbox-dist

ln -s /opt/dropbox ~/.dropbox-dist

Run dropbox as a normal user /opt/dropbox/dropboxd this will give you a link to login to your account or if you are running a GUI it will open your default browser with a link to authenticate your account. Ctrl-c to stop dropboxd. As I said earlier, I want to put my Dropbox files on my storage drive. In my case I am using a ZFS drive located at /pool/shared/ I moved the Dropbox folder from my home folder to /pool/shared/Dropbox then linked the Dropbox folder to my home folder.

mv ~/Dropbox /pool/shared/Dropbox
ln -s /pool/shared/Dropbox ~/Dropbox

Now if you want to control dropbox from the command-line you can use this tool

wget -O dropbox-cli.py https://www.dropbox.com/download?dl=packages/dropbox.py

You can move it to your PATH for easy access. You have to have ~/.dropbox-dist, ~/.dropbox, and ~/Dropbox to use this tool. You can start/stop and check the status of the Dropbox service. The ~/.dropbox folder is created after you start dropboxd. For systemd to autostart Dropbox service simply create a new service at /etc/systemd/system/dropbox@.service with these configurations:

[Unit]
Description=Dropbox
After=local-fs.target network.target

[Service]
Type=simple
ExecStart=/opt/dropbox/dropboxd -p /home/%I/.dropbox/dropbox.pid
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
User=%I

[Install]
WantedBy=multi-user.target

This is basically a systemd service that can control Dropbox daemon from command-line. You can enable/start Dropbox from systemd sudo systemctl start dropbox@$USER.service or if you want to enable it to start on boot sudo systemctl enable dropbox@$USER.service


Leave a Comment



What is Jekyll?

09 Jun 2017 0 Comments

Jekyll is a static website builder, it assembeles and combines multiple pages to form one unified HTML page. Jekyll uses YAML language to organize the structure of the page. Markdown language, which is a text-to-HTML converter, makes webpages easy to write and adapt.

Jekyll is writen in Ruby and can be installed using gem. On a Linux machine that already has Ruby installed, Jekyll can be installed as the following:

gem install jekyll bundler
jekyll new my-awesome-site
cd my-awesome-site
bundle exec jekyll serve

Now you can visualize the page using http://localhost:4000


Leave a Comment