Write in C (ZX Spectrum) GameDev

This ne’er-do note is devoted to the development of games for the ZX Spectrum old computer to C. Let’s take a look at the handsome:

He began producing in 1982, and was produced until 1992. Technical data: 8-bit processor Z80, 16-128kb memory and other are extensions such as audio chipAY-3-8910.

The competition Yandex Retro Games Battle 2019 for this machine I wrote a game called Interceptor 2020. Since learning assembler for the Z80 did not have time, I decided to develop it in C language. As tulcheyna I chose the Quick Set – z88dk, which includes C compilers, and many support libraries to accelerate the implementation of applications for the Spectrum. It also supports many other Z80 machines, such as MSX, Texas Instruments calculators.

Next I will describe his flight over the surface computer architecture tulcheynom z88dk, show how it was possible to implement OOP approach is to use design patterns.

Special features

z88dk installation should be performed by a manual from the repository, but for Ubuntu users, I would like to mention feature – if you have already installed compilers for Z80 from the deb package, you should remove them as z88dk will default to access them from the bin folder of the -this version incompatibility tulcheyn compiler, you probably will not be able to collect anything.

Hello World

Write Hello World is very simple:


#include void main()
{
    printf("Hello World");
}

Assemble the tap in the file is even easier:


zcc +zx -lndos -create-app -o helloworld helloworld.c

To start using any emulator ZX Spectrum tap supporting files, such as Online:

http://jsspeccy.zxdemo.org/

Draw on the image to full screen

tl; dr Pictures drawn tiles, tiles 8×8 pixels size, the tiles themselves are embedded in the font Spectrum, then the string of index picture is printed.

O library sprites and tiles sp1 displays tiles using UDG. The picture is translated into a set of separate UDG (tiles) is then collected on the screen using the indices. It should be remembered that UDG is used to display the text, and if your image contains very large set of tiles (eg more than 128 tiles), you have to go beyond the set boundaries and to erase the default font Spectrum. To work around this limitation, I used a base of 128 – 255 using the simplified representation, leaving the original font on the spot. On simplification of the pictures below.

To draw a full-screen images you need to arm the three utilities:
Gimp
img2spec
png2c-z88dk

There is a way ZX real men, real retro warriors is to open the editing palette using Spectrum, especially knowing the output image, prepare it and manually unload using png2c-z88dk or png2scr.

Way easier – take a 32-bit image, switch to Gimp colors to 3-4, slightly to edit, then import into img2spec not to work by hand with color restrictions, export png and transferred to the C array with png2c-z88dk.

It should be remembered that successful export each tile can not contain more than two colors.

As a result, you get the h file that contains a number of unique tiles, if more than ~ 128, simplify the Gimp in a picture (increase repeatability) and spend on a new procedure for export.

After exporting, you literally download a “Font” from the tiles and typing “text” of the indices of tiles on the screen. Here is an example of the “class” of the rendering:


// loading font into memory
    unsigned char *pt = fullscreenImage->tiles;

    for (i = 0; i < fullscreenImage->tilesLength; i++, pt += 8) {
            sp1_TileEntry(fullscreenImage->tilesBase + i, pt);
    }

    // set cursor into 0,0
    sp1_SetPrintPos(&ps0, 0, 0);

    // print string
    sp1_PrintString(&ps0, fullscreenImage->ptiles);

Drawing sprites on the screen

Next, I will describe a way of drawing sprites of 16×16 pixels on the screen. Before the animation and change colors I came because corny at this stage, I guess I ran out of memory. Therefore, in the game there are only transparent monochrome sprites.

Draw in Gimp monochrome png image 16×16, etc. using png2sp1sprite translate it into assembly asm file in C code declare arrays of the assembly file, add the file at build time.

After the declaration of a resource of the sprite, it is necessary to add the screen to the desired position, then the sample code “class” game object:


    struct sp1_ss *bubble_sprite = sp1_CreateSpr(SP1_DRAW_MASK2LB, SP1_TYPE_2BYTE, 3, 0, 0);
    sp1_AddColSpr(bubble_sprite, SP1_DRAW_MASK2,    SP1_TYPE_2BYTE, col2-col1, 0);
    sp1_AddColSpr(bubble_sprite, SP1_DRAW_MASK2RB,  SP1_TYPE_2BYTE, 0, 0);
    sp1_IterateSprChar(bubble_sprite, initialiseColour);

From the names of some functions can understand the meaning – allotsiruem memory sprite, add two columns 8×8, add color for a sprite.

Each frame is affixed position of the sprite:


sp1_MoveSprPix(gameObject->gameObjectSprite, Renderer_fullScreenRect, gameObject->sprite_col, gameObject->x, gameObject->y);

OOP simulations

In C, there is no syntax for OOP, what do you do if you still really want to? It is necessary to connect the Dumka and illumined thought that such a thing as the PLO equipment does not exist, everything eventually comes to a machine architectures in which there is simply no concept of the object and other related abstractions.

This fact bothered me for a long time to understand why do you need the PLO, why you need to use it if in the end everything comes to machine code.

However, having worked in the product development, I opened the charm of this programming paradigms, primarily of course the development of the flexibility mechanisms of the protective code, with the right approach entropy reduction, simplification of teamwork. All of these advantages derive from the three pillars – polymorphism, encapsulation, inheritance.

Also worth noting is the simplification of addressing issues related to the architecture of the application, because 80% of architectural problems were solved by computer-scientists in the last century and described in the literature devoted to the design pattern. Next, I will describe how to add like OOP syntax in C.

For instance data storage more convenient to take the basis of class C structure. Of course, you can use a byte buffer to create its own structure for the classes, methods, but why reinvent the wheel? After all, we already reinventing syntax.

These classes

An example of the data fields “class” GameObject:


struct GameObjectStruct {
    struct sp1_ss *gameObjectSprite;
    unsigned char *sprite_col;
    unsigned char x;
    unsigned char y;
    unsigned char referenceCount;
    unsigned char beforeHideX;
    unsigned char beforeHideY;
};
typedef struct GameObjectStruct GameObject;

We maintain our class as “GameObject.h” do #include “GameObject.h” in the right place and use.

Class methods

Take into service experience Objective-C language development, the signature method of the class will be from a function in the global osprey, the first argument will always transmitted data structure, method arguments go further. Next, an example of “the method” “class” GameObject:


void GameObject_hide(GameObject *gameObject) {
    gameObject->beforeHideX = gameObject->x;
    gameObject->beforeHideY = gameObject->y;
    gameObject->y = 200;
}

Method call is as follows:


GameObject_hide(gameObject);

Constructors and destructors are implemented in the same manner. It can be implemented as an allocator constructor and field initializers, but I prefer separate methods for that,

Working with memory

Manual memory management type using malloc and free macros wrapped in new and delete operators for compliance with C ++:


#define new(X) (X*)malloc(sizeof(X))
#define delete(X) free(X)

For objects that are used by multiple classes at once, realized semi-manual memory management based on reference counting, in the image of the old mechanism of Objective-C Runtime ARC:


void GameObject_retain(GameObject *gameObject) {
    gameObject->referenceCount++;
}

void GameObject_release(GameObject *gameObject) {
    gameObject->referenceCount--;

    if (gameObject->referenceCount < 1) { sp1_MoveSprAbs(gameObject->gameObjectSprite, &Renderer_fullScreenRect, NULL, 0, 34, 0, 0);
        sp1_DeleteSpr(gameObject->gameObjectSprite);
        delete(gameObject);
    }
}

Thus, each class must declare the use of a common object using the retain, release the possession through release. In the modern version of ARC uses an automatic affixing call retain / release.

I sound!

Spectrum has a tweeter capable of reproducing 1-bit music, the composers of the time were able to play on it for up to 4 audio channels simultaneously. Spectrum 128k comprises a separate sound chip AY-3-8910, which can reproduce music tracker. To use the Tweeters in z88dk proposed library sound.h

What is to be learned

I was interested to read the Spectrum, to realize the game z88dk means, learn a lot of interesting things. I much remains to be explored, such as the assembler Z80, as it allows you to use the full power of Spectrum, the work of memory banks, working with the sound chip AY-3-8910. I hope to participate in the competition for next year!

References

https://rgb.yandex
https://vk.com/sinc_lair
https://www.z88dk.org/forum/

Source Code

https://gitlab.com/demensdeum/zx-projects/tree/master/interceptor2020

Binary Search

Suppose we need to find out whether the email address “ demensdeum@gmail.com ” belongs to the list of allowed email addresses for receiving letters.

We sort through the entire list from the first to the last element, checking whether the element is equal to the specified address – we implement the linear search algorithm. But will it be long or not?

To answer this question, use the “Temporary complexity of the algorithms”, “O” notation. In the worst case, the linear search operation time is equal to the nth number of array elements, we write this in the “O” notation – O (n). Next, you need to clarify that for any known algorithm, there are three performance indicators – execution time in the best, worst, and average cases. For example, the mail address “ demensdeum@gmail.com ” is in the first index of the array, then it will be found in the first step of the algorithm, it follows that the execution time in at best, O (1); and if at the end of the list, then this is the worst case – O (n)

But what about the details of the software implementation, the performance of the hardware, should they affect big O? Now exhale and imagine that the calculation of time complexity is calculated for some abstract ideal machine, in which there is only this algorithm and nothing more.

Algorithm

Ok, it turns out that the linear search is rather slow, let’s try using the binary search. To begin with, it should be clarified that we will not work with binary data, this name was given to this method because of the features of its work. Initially, we sort the array into lexicographically , then the algorithm takes the range of the entire array, gets the middle element of the range, compares it lexicographically , and depending on the result comparison decides what range to take to search for more – the top half of the current or lower. That is, at each step of the search, a decision is made of two possible ones – binary logic. This step is repeated until either the word is found or not found (the intersection of the lower and upper indices of the range will occur).

The performance of this algorithm is the best case when an element is immediately found in the middle of the O (1) array, the worst case is O (log n)

Gotchas

When implementing binary search, I came across not only the interesting problem of the lack of standardization of lexicographic comparison in programming language libraries, but I even found that there is no single standard for implementing localeCompare inside JavaScript . The ECMAScript standard allows different implementations of this function, because of this, when sorting using localeCompare, absolutely different results can be observed on different JavaScript engines.

Therefore, for the algorithm to work correctly, you must sort and use only the same lexicographic comparison algorithm in your work, otherwise nothing will work. So, for example, if you try to sort an array in Scala, and search using nodejs without realizing your own sorting / sorting of one implementation, then you will not expect anything other than disappointment in humanity.

References

https://en.wikipedia.org/wiki/Lexicographical_order
https://en.wikipedia.org/wiki/Binary_search_algorithm
https://stackoverflow.com/questions/52941016/sorting-in-localecompare-in-javascript

Source Code

https://gitlab.com/demensdeum/algorithms

Facade Pattern

Facade belongs to structural design patterns. It provides a single interface that provides interaction between client and complex systems. GoF has a good example of the Facade – a compiler of programming languages ​​that provides different clients with different goals, the ability to build code through a single interface of the compiler facade.

References

https://refactoring.guru/design-patterns/facade
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Abstract Factory Pattern

Abstract factory – provides an interface for creating the corresponding objects, without specifying specific classes.

I really like the alternative name for this pattern – Kit

It is very similar to the Factory Method, however, Abstract Factories should describe the relationship between the created objects, otherwise it is already just the antipattern God Object, creating unsystematically everything.

Imagine the development of an AR framework for glasses, we display on the screen arrows for indoor navigation, store icons, interesting places, windows and buttons with information about any place where the user is now located.

At the same time, we need the ability to customize the appearance and behavior of the controls of the AR environment. That’s it for this case, you need to use the Set pattern.

We’ll write the interface of Abstract Factory and Products – parent protocols, elements of the AR environment:

 
protocol ARFactory {
    func arrow() -> ARArrow
    func icon() -> ARIcon
    func button() -> ARButton
    func window() -> ARWindow
}

protocol ARArrow {
    var image: { get }
    func handleSelection()
}

protocol ARIcon {
    var image: { get }
    var title: String
}

protocol ARButton {
    var title: String
    func handleSelection()
}

protocol ARWindow {
    var title: String
    var draw(canvas: Canvas)
}
 

Now the kit developers will need to implement the Concrete Factory on the basis of the Abstract Factory interface, and all the elements will have to be implemented together, the rest of the application will be able to work with the factory without changing its code.

References

https://refactoring.guru/design-patterns/abstract-factory
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Factory Method Pattern

Pattern Factory Method refers to creational design patterns.
This pattern describes the creation of an interface for creating an object of a particular class. Sounds simple yeah?

Theory

Suppose we are developing a framework for working with AR glasses, when you tilt your head to the side, a menu of available applications should appear in front of the user’s eyes. Applications will be developed by third-party companies, clients of our framework. Naturally, we don’t know which applications, icons, names should appear, so we must provide an interface for implementing the icon and related information about the application. Call it Product:

 
protocol Product {
 var name: String { get }
 var image: Image { get }
 var executablePath: String { get }
}
 

Next, we need to provide an interface so that our customers implement the application array of their Specific Product – an array of application icons with names that we will already draw in the framework.

We’ll write this interface – the Creator interface, containing the Factory Method, which returns an array of Products.

 
protocol Creator {
 func factoryMethod() -> [Product]
}
 

In practice

The first customer of our AR framework was 7B, a leading provider of coffee maker software in Honduras. They want to sell augmented reality glasses with the ability to brew coffee, check the fullness of the water / beans, point the way to their nearest coffee maker in indoor map mode.

They undertake the development of software, we only need to provide documentation on the interfaces of Creator and Product, for the correct listing of applications and their further launch.

After the documentation is transferred, 7B using the Creator interface implements the Concrete Creator – a class that returns an array of icon applications. Icon applications themselves are the Concrete Product classes implementing the Product interface.

Concrete Product code example:

 
class CoffeeMachineLocator: implements Product {
 let name = “7B Coffee Machine Locator v.3000”
 let image = Image.atPath(“images/locator.tga”)
 let executablePath = “CoffeeMachineLocator.wasm”
}

class iPuchinno: implements Product {
 let name = “iPuchinno 1.0.3”
 let image = Image.atPath(“images/puchino.pvrtc”)
 let executablePath = “neutron/ipuchBugFixFinalNoFreezeFixAlpha4.js”
}
 

Concrete Creator class, returning array with two applications:

 
class 7BAppsCreator: implements Creator {
 func factoryMethod() -> [Product] {
  return [CoffeeMachineLocator(), iPuchinno()]
 }
}
 

After that, 7B compiles the library of Concrete Products, Concrete Creator and combines it with our framework, starts selling AR glasses for its coffee makers, no source code modifications on our side is required.

References

https://refactoring.guru/design-patterns/command
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Command Pattern

Pattern Command refers to behavioral design patterns.

This is the pattern with which I sit longer than the rest, it is so simple that it is very complicated. But personally, I find the charm of self-learning in that you have all the time in the world to explore a specific issue from all angles.

So, in GoF, applicability is described rather concisely and clearly:
Encapsulates the request as an object, allowing you to configure (parameterize) clients with different requests, use queues, log requests and perform cancellation operations.

Now we implement a simple version of the command from the description:

 
string fakeTrumpsRequest = “SELECT * from Users where name beginsWith DonaldTrump”
 

We encapsulated the request in a string class object, it can configure clients, add commands to the queue, log, cancel (using the “Snapshot” pattern)

It seems to me that this is quite enough to implement SQL queries and the like, but then the implementation details begin, various applications, the code base of the pattern, the roles of the clients vary greatly, auxiliary classes are added.

The Math Part

The command pattern begins with the Command protocol, which contains a single execute() method. Next comes the Concrete Command and Receiver, QC implements the operation on the Receiver, describes the relationship between the Receiver and the action. Nothing is clear? Me too, but drove on. Client creates an instance of the Specific Command, associates it with the Receiver. Invoker – an object that implements the process of launching Command.

Now let’s try to figure out an example, let’s say we want to update myOS on myPhone, for this we launch the application myOS_Update!, in it we press the Update Now! Button, after 10 seconds the system will inform you of a successful update.

The Client in the example above is the application myOS_Update!, Invoker is the “Update Now!” button, it launches the Specific Command of the system update using the execute(), which refers to the Receiver – the daemon for updating the operating system.

Case Study

Let’s say the application UI is myOS_Update! so good that they decided to sell it as a separate product to provide an update interface for other operating systems. In this case, we will implement an application with support for extension through libraries, in the libraries there will be implementations of Specific Commands, Receivers, we will leave static/unchanged Invoker, Client, protocol Command.

Thus, there is no need to provide support for mutable code, since our code will remain unchanged, problems can arise only when implemented on the client side, due to errors in the code of their Concrete Commands and Receivers. Also, in such an implementation, there is no need to transfer the source code of the main application, that is, we encapsulated commands and UI interactions using the Command pattern.

References

https://refactoring.guru/design-patterns/command
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Crosscompile for macOS on Ubuntu OSXCross CMake

In this note, I will describe the assembly of cross-platform C ++ applications for macOS on an Ubuntu build machine using CMake and osxcross.
First, install the osxcross toolchain:
https://github.com/tpoechtrager/osxcross
Installation takes place in 3 stages, loading dependencies:

 
cd tools
./get_dependencies.sh
 

Download XCode.xip from the official Apple website, then download the SDK from Xcode:

 
./gen_sdk_package_pbzx.sh /media/demensdeum/2CE62A79E62A4404/LinuxSupportStorage/xcode111.xip
 

I hope you read the Xcode license agreement in the last step? Next, building the toolchain with the desired prefix:

 
INSTALLPREFIX=/home/demensdeum/Apps/osxcross ./build.sh 
 

Now you can use osxcross from the prefix directory of the last step. Add a new build macro for CMake, write everything you need:

 
if (OSXCROSS)
SET(CMAKE_SYSTEM_NAME Darwin)
SET(CMAKE_C_COMPILER o64-clang)
SET(CMAKE_CXX_COMPILER o64-clang++)
SET(CMAKE_C_COMPILER_AR x86_64-apple-darwin19-ar)
SET(CMAKE_CXX_COMPILER_AR x86_64-apple-darwin19-ar)
SET(CMAKE_LINKER x86_64-apple-darwin19-ld)
SET(ENV{OSXCROSS_MP_INC} 1)
endif()
 

I did not succeed in dynamic linking, so we export the libraries statically:

 
if (OSXCROSS)
add_library(FlameSteelCore STATIC ${SOURCE_FILES})
else()
 

Further, you may encounter the fact that you do not have the necessary libraries for osxcross, I encountered this when using SDL2. osxcross supports ready-made library packages – macports. For example, installing SDL2-mixer:

 
osxcross-macports -v install libsdl2_mixer
 

After that, you can start assembling libraries / applications as usual in the cmake-make link, do not forget to register a static link of libraries if necessary.

Manual assembly of libraries

At the moment, I have encountered the problem of incorrect archiving of libraries during static linking, I get an error when building the final application:

 
file was built for archive which is not the architecture being linked (x86_64)
 

Very similar to this ticket , I managed to implement workaround in as a result, the build completes correctly. Unzip the static library and build it again using the osxcross archiver:

 
ar x ../libFlameSteelCore.a
rm ../libFlameSteelCore.a
x86_64-apple-darwin19-ar rcs ../libFlameSteelCore.a *.o
 

Also, I personally consider one of the problems the lack of the ability to run macOS applications immediately on ubuntu (at least with a part of the functionality), of course there is a project darling , but support is poor for the time being.

References

https://github.com/tpoechtrager/osxcross

Crosscompile for Windows on Ubuntu MinGW CMake

In this article, I will describe the process of building libraries and applications for Windows using the MinGW32 toolchain on Ubuntu.
Install wine, mingw:


sudo apt-get install wine mingw-w64

After that, you can already build C / C ++ applications for Windows:


# C
i686-w64-mingw32-gcc helloWorld.c -o helloWorld32.exe      # 32-bit
x86_64-w64-mingw32-gcc helloWorld.c -o helloWorld64.exe    # 64-bit
 
# C++
i686-w64-mingw32-g++ helloWorld.cc -o helloWorld32.exe     # 32-bit
x86_64-w64-mingw32-g++ helloWorld.cc -o helloWorld64.exe   # 64-bit

Collected exe can be checked with wine.

Next, consider the changes to the CMake assembly, the CMakeLists.txt file, add MinGW specific things to the assembly file:


if (MINGW32)
set(CMAKE_SYSTEM_NAME Windows)
SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
set(CMAKE_RANLIB i686-w64-mingw32-ranlib)
endif()

elseif (MINGW32)
add_library(FlameSteelEngineGameToolkit.dll SHARED ${SOURCE_FILES})
else()

// link with all dependencies
if (MINGW32)
target_link_libraries(
                        FlameSteelEngineGameToolkit.dll 
                        -static-libgcc
                        -static-libstdc++
                        SDL2 
                        SDL2_mixer 
                        /home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FlameSteelCore/FlameSteelCore.dll
                        /home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FlameSteelBattleHorn/FlameSteelBattleHorn.dll
                        /home/demensdeum/Sources/cube-art-project-bootstrap/FlameSteelFramework/FlameSteelCommonTraits/FlameSteelCommonTraits.dll)

set_target_properties(FlameSteelEngineGameToolkit.dll PROPERTIES
        PREFIX ""
        SUFFIX ""
        LINK_FLAGS "-Wl,--add-stdcall-alias"
        POSITION_INDEPENDENT_CODE 0 # this is to avoid MinGW warning; 
        # MinGW generates position-independent-code for DLL by default
)
else()

Build:


cmake -DMINGW32=1 .
make

The output will be dll or exe, depending on your build. You can look at the working example in the repository of the new Cube-Art-Project and its libraries:
https://gitlab.com/demensdeum/cube-art-project
https://gitlab.com/demensdeum/FlameSteelEngineGameToolkitFSGL
https://gitlab.com/demensdeum/cube-art-project-bootstrap

References
https://arrayfire.com/cross-compile-to-windows-from-linux/