Tuesday, September 15, 2020

SGDK Programming Setup

In 2013, we checked out Sega Retro Gaming, specifically 8-bit hardware manufactured by Sega such as the SG-1000, SC-3000 and Sega Master System. Now, we turn our attention to the 16-bit Mega Drive [Genesis].

In 2017, we checked out devkitSMS Programming Setup to build 8-bit game code for the Master System. As we upgrade to the 16-bit MD we would like to replicate this process now using Sega Genesis Dev Kit [SGDK].
Let’s check it out!

SGDK
The SGDK is a free development kit allowing you to develop software in C language for the Sega Mega Drive. Note that SGDK also requires Java (custom tools need it) so you need to have Java installed on your system.

IMPORTANT
Ensure an up-to-date version of Java is installed, e.g. version "12.0.2" 2019-07-16 otherwise builds may fail!

Software
Follow most instructions from the previous post: this documents how to setup some pre-requisite software.

Here is a summary of some of the software to be installed:
 Name Version
 C IDE Editor Visual Studio 2015
 Emulators Fusion, Gens KMod
 
 Name Version
 Cross compiler GCC
 Make files Cygwin

Gens KMod "gens" can also be built from source. Add gens.exe to Gens folder similar to Fusion in this post. Note: you may already have gcc configured on you computer if you previously installed cygwin or Git Bash

SGDK
Navigate to the SGDK repository on github: @MegadriveDev has full instructions here. Download the latest release from SGDK archive e.g. 1.51 or simply git clone the latest source code. Extract code into C:\SGDK.

Setup the following environment variables for GDK + GDK_WIN to be used throughout SGDK development. Add the following two environment variables: System | Advanced system settings | Environment Variables:
 Variable  Value  Description
 GDK  C:/SGDK  UNIX path format
 GDK_WIN  C:\SGDK  Windows path format

Next, Stephane advises to add the %GDK_WIN%\bin to your PATH variable being careful if you have another GCC installation as you may have conflicts. I have multiple GCC installs but have not experienced any issues.

Finally, generate the SGDK library %GDK%/lib/libmd.a by entering the following command Start | run | cmd.
%GDK_WIN%\bin\make -f %GDK_WIN%\makelib.gen

Example
As an example, let's write a simple program that prints "Hello World" using SGDK. Create new directory: C:\HelloWorld. Create main.c file + enter the following code similar to the Hello World tutorial program.

main.c
#include <genesis.h>
int main()
{
	VDP_drawText( "Hello Genny World!", 10, 13 );
	while( 1 )
	{
		VDP_waitVSync();
	}
	return 0;
}

Build
Manually compile, link and execute the Hello World program. Launch command prompt: Start | Run | cmd.

Change directory cd C:\HelloWorld. Next, execute the following make command to produce the output ROM:
 ACTION  COMMAND  OUTPUT
 Compile  %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen  out\rom.bin

Finally, type out\rom.bin. The Hello World program should launch in the Fusion emulator.
Congratulations! You have just written your first Mega Drive program using the SGDK.

Automate
Let's automate the build process: create C:\HelloWorld\build.bat script file that contains the commands:
@echo off
%GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen
out\rom.bin

Code Blocks
Follow all instructions here to setup SGDK with CodeBlocks. @matteusbeus has great video on how to setup SGDK using CodeBlocks also. Check out his YouTube channel for other interesting videos with SGDK topics.

Eclipse
Follow all instructions here to setup SGDK with Eclipse. @SpritesMind has a great tutorial on how to remote debug SGDK code using Gens KMod with Eclipse also. Follow all instructions here to complete debug setup:

Launch Eclipse. Choose Run menu | Debug Configurations | C/C++ Remote Application | New Configuration
 Name  Remote Debugging
 Project  HelloWorld
 C/C++ Application  C:\HelloWorld\out\rom.out
 Disable auto build  CHECKED
IMPORTANT ensure "Using GDB (DSF) Manual Remote Debugging Launcher" is preferred launcher selected!

 Stop on startup as: main  CHECKED
 GDB debugger  C:SGDK\bin\gdb.exe
 GDB command line  .gdbinit

 Type  TCP
 Host name or IP address  localhost
 Port number  6868
Build project. Load rom.bin in gens. Set breakpoints. Click debug. Hit Tab in gens to refresh + debug code!

Visual Studio Code
@ohsat_games has a great tutorial on how to setup SGDK using Visual Studio Code. You can even install the Genesis Code extension for code auto completion and to streamline the build process all from within the IDE.

Further SGDK tutorials from @ohsat_games can be found here. All projects have a similar build batch script:
%GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen

Visual Studio 2015
Retro Mike has a great video on how to setup SGDK using Visual Studio 2015. Launch Visual Studio 2015. File | New | Project... | Visual C++ | General | Makefile Project. Enter the following name + location info:
 Name:  HelloWorld
 Location:  C:\
 Create directory for solution  UNCHECKED

Choose Next. Enter the following Build command: %GDK_WIN%\bin\make -f %GDK_WIN%\makefile.gen

Choose Finish. Enter the previous "Hello World" code as above. Finally, right click Project | Properties | VC++ Directories | Include Directories. Enter the following in order to include SGDK header files: $(GDK_WIN)\inc

Press Ctrl + Shift + B to build source code and produce out\rom.bin. Launch Fusion emu + load ROM file! Alternatively, setup "Remote" debugging configuration in Visual Studio and Press Ctrl + F5 to build and run.

Right click Project | Properties | Debugging | Remote Windows Debugger. Update in the following properties:
 Remote Command  C:\SEGA\Fusion\Fusion.exe
 Remote Command Arguments  out\rom.bin
 Connection  Remote with no authentication
 Debugger Type  Native Only
 Attach  Yes


Summary
To summarize, there are multiple IDE setups available to use the SGDK. However, despite VS2015 being the most applicable to our previous devkitSMS process, we're still not able to debug step thru the C source code.

Ultimately, we would like to build + debug step thru the C source code during SGDK development whilst also being able to build + run the ROM output file all from in Visual Studio. This will be the topic of the next post.

Monday, August 31, 2020

Python Setup Cheat Sheet II

In the previous post we checked out Python Setup Cheat Sheet to download and install Python and setup an integrated development environment for Python programming. We installed open source Python distribution Anaconda used for data science. Let's continue setup to build artificial intelligence + machine learning apps.

Let's check it out!


Install VS Code
VS Code is an integrated development environment with useful plugins for Python and Data Science. Install VS Code from Anaconda navigator. Otherwise install VS Code for Windows and Mac OS/X and Linux directly.

Launch VS Code. Install Python extension for Visual Studio Code. Install other plugins for example Remote SSH to connect to Linux VM or Code Runner. Install VS Code Insiders to try pre-release version of VS Code.

Update global settings.json file to your particular preferences. Here are some common VS Code examples:
 SYSTEM  LOCATION
 Windows  %APPDATA%/Code/User/settings.json
 Mac OS/X  ~/Library/Application Support/Code/User/settings.json
 Linux  ~/.config/Code/User/settings.json
Note: VS Code Insiders location replace Code/User/settings.json with Code - Insiders/User/settings.json

settings.json
 {
    "workbench.colorTheme": "Visual Studio Light",
    "window.zoomLevel": 0,
    "editor.trimAutoWhitespace": false,
    "editor.renderIndentGuides": false,
    "editor.roundedSelection": false,
    "editor.suggestSelection": "first",
    "python.dataScience.sendSelectionToInteractiveWindow": true,
    "python.jediEnabled": false,
    "code-runner.clearPreviousOutput": true,
    "code-runner.runInTerminal": true,
    "code-runner.showExecutionMessage": false,
    "code-runner.respectShebang": false,
    "code-runner.defaultLanguage": "python3",
 }

 Windows  Mac OS/X + Linux
 {
    "python.pythonPath": "%USERPROFILE%/Anaconda3/python.exe",
    "code-runner.executorMap": {
        "python": "python.exe",
        "python.pythonPath": "%USERPROFILE%/Anaconda3",
    },
 }
 {
    "python.pythonPath": "/anaconda3/bin/python",
    "code-runner.executorMap": {
        "python": "python",
        "python.pythonPath": "/anaconda3/bin",
    },
 }


Hello VS Code
Create folder "HelloVScode". Launch VS Code. File | Open Folder | HelloVScode | Select Folder. Create simple "HelloWorld.py" file. Enter simple code print('Hello World'). Press F5 to debug Python script. Customize Run + Debug click "create a launch.json file". Click Python File: Debug the currently active Python file. Press F5.

IMPORTANT
Press Ctrl + Shift + P | Python: Select Interpreter. If creates new workspace settings.json + override Python interpreter user settings.json then do not check python.pythonPath into source control when deploying cross platform.

Virtual Environment
Unlike PyCharm, VS Code does not automagically create an isolated Python environment for new projects. Therefore, follow all instructions here to setup and activate virtual environment for Python using VS Code.

CommandNotFoundError
When running Python script using Anaconda you may get error CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'. Terminal | Click "+" | Spawn new Terminal default "Conda".

Code Runner
Automate process by installing Code Runner plugin. Press F1 | Run Code first time to set Output to "Code". Ensure entries above in user settings.json for fast turnaround. Press Ctrl + Alt + N for each subsequent run!

IMPORTANT
On Mac OS/X if Ctrl + Alt + N does not work then you may have to swap Ctrl for Cmd in keybindings.json:
~/Library/Application Support/Code/User/keybindings.json
// Place your key bindings in this file to override the defaultsauto[]
[
    {
        "key": "cmd+alt+n",
        "command": "code-runner.run"
    },
    {
        "key": "cmd+alt+n",
        "command": "-code-runner.run"
    }
]


Code Sample
Let's test drive develop a simple code sample as a Python module that could be deployed as Python package.

IMPORTANT
A module is a single Python script file whereas a package is a collection of modules. A package is a directory of Python modules containing an additional __init__.py file to distinguish from a directory of Python scripts.

Create folder "PackageVScode". Launch VS Code. File | Open Folder | PackageVScode. Create New Folder "MyPackage". Create other top level folders, for example, Build + Docs etc. Create hidden .vscode folder.

Create sub folders src and tests beneath "MyPackage". Create requirements.txt file + setup.py beneath "MyPackage" also. Finally, create module.py and __init__.py under src and test_module.py under tests.

 test_module.py  module.py
 import unittest
 from MyPackage.src.module import add_one

 class TestSimple(unittest.TestCase):

    def test_add_one(self):
        result = add_one(5)
        self.assertEqual(result, 6)

 if __name__ == '__main__':
    unittest.main()
 def add_one(number):
    return number + 1

Add launch.json to .vscode folder. Accept default to launch current Python file from integrated terminal:
launch.json
 {
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        }
    ]
 }

Open test_module.py. Press F5 to debug. Output ModuleNotFoundError: No module named 'MyPackage'. In VS Code you must correctly set PYTHONPATH in order to debug step through and run local source code!

Add settings.json to .vscode folder. Configure Python source folders by configuring the env PYTHONPATH:
settings.json
 {
    "terminal.integrated.env.windows": {
        "PYTHONPATH": "${env:PYTHONPATH};${workspaceFolder}"
    },
 "terminal.integrated.env.osx": {
        "PYTHONPATH": "${env:PYTHONPATH}:${workspaceFolder}",
    },
    "terminal.integrated.env.linux": {
        "PYTHONPATH": "${env:PYTHONPATH}:${workspaceFolder}",
    },
    "python.envFile": "${workspaceFolder}/.env"
    ]
 }

Finally, you must add "hidden".env file beneath "PackageVScode" that sets workspace folder per platform:
 WORKSPACE_FOLDER=/Absolute/Path/to/PackageVScode/
 PYTHONPATH=${WORKSPACE_FOLDER}

IMPORTANT
The "hidden".env file "lives" locally to project and should NOT be checked into source code version control! It may also be necessary to close VS Code and re-open after configuring .env file. Windows must use "/".

Now press F5 to debug test code or press Ctrl + Alt + N for Code Runner. Alternatively, Terminal command:

python -m unittest discover MyPackage

IMPORTANT
If the Terminal reveals Run 0 tests then ensure package has __init__.py setup in every relevant sub folder. Finally, enter dependencies for requirements.txt file and setup.py e.g. numpy. Install package at terminal:
 setup.py  requirements.txt
 import setuptools

 with open("README.md", "r") as fh:
    LONG_DESCRIPTION = fh.read()

 setuptools.setup(
    name='MyPackage',
    version='0.1.2',
    description="My test package.",
    long_description=LONG_DESCRIPTION,
    long_description_content_type="text/markdown",
    packages=setuptools.find_packages(),
    install_requires=[
        "numpy>=1.17.1",
    ]
 )
 numpy>=1.17.1
pip install MyPackage/.

Finally, we could also replicate the unit test code directly on the REPL. Select Terminal tab. Enter commands:

 python
 >>> from MyPackage.src.module import add_one
 >>> result = add_one(5)
 >>> print(result)

Code Linting
Linting highlights syntactical and stylistic problems in Python source code which helps identify and correct subtle programming errors. In VS Code, navigate to Python script via Terminal e.g. Pylint test_module.py.

Alternatively, select Terminal and type specific linter like flake8 to check Python source code against PEP8 coding style programming errors. If flake8 is not installed then simply type pip install flake8 at Terminal.


Jupyter Notebooks
Notebooks are becoming the standard for prototyping and analysis for data scientists. Many cloud providers and Anaconda navigator offer machine learning and deep learning services in the form of Jupyter notebooks.

Launch Anaconda navigator | Choose Jupyter Notebook | Launch. After the browser launches create a New | Python 3 | Jupyter notebook. Follow tutorial. Change browser from Google Chrome to Firefox if any issues! Alternatively, create notebook in VS Code | Ctrl + Shift + P | Python: Create New Blank Jupyter Notebook.


Remote SSH
Often machine learning AI projects may require remote development in VS Code to use Windows to develop in a Linux-based environment. Install Remote SSH to run and debug Linux-based applications on Windows. Start | run | cmd. ssh username@linux_server. Enter passphrase. Enter verification code if setup for MFA.

SSH Keys
Use SSH key authentication and setup SSH keys to connect local Windows host and remote Linux VM server.
 Windows  Linux
 cd %USERPROFILE%
 cd .ssh
 ssh-keygen -C "username@emailaddress.com"
 ssh username@linux_server
 cd ~/.ssh
 ssh-keygen -C "username@emailaddress.com"

Dump the contents of id_rsa.pub file from local Windows host to authorized_keys file on Linux VM server:
cd ~/.ssh
echo "contents_of_Windows_id_rsa.pub_file" >> authorized_keys

Finally, configure %USERPROFILE%\.ssh\config file with Linux VM server information to alias connection.
 Host ENVIRONMENT
     HostName linux_server
     User username
     IdentityFile ~/.ssh/id_rsa

SSH Tunnel
Port forwarding via SSH Tunnel creates a secure connection between the local computer and remote machine through which services can be relayed. SSH Tunnel is useful for transmitting data over encrypted connection.
 ssh -N -L localhost:8787:LoadBalancer_External-IP:8000 username@linux_server

The same technique can be uesd to access Jupyter Notebook on Linux VM server. Launch terminal and enter:
 ssh username@linux_server
 cd ~/
 jupyter notebook --no-browser --port=8889
 ssh -N -L localhost:8889:localhost:8889 username@linux_server

WinSCP
Install WinSCP as popular SFTP client to navigate + copy files between local Windows and remote Linux VM. Launch WinSCP. Enter Host Name, Port number, User name, Password and verification code if setup for MFA.


Summary
To summarize, we have setup Python distribution Anaconda on Windows, Mac OS/X and Linux to now build artificial intelligence and machine learning apps. We are now set to develop machine learning models then deploy using Flask API. Apps can then be containerized using Docker and orchestrated using Kubernetes to significantly increase the efficiency of a Continuous Integration / Continuous Deployment infrastructure J

Saturday, July 4, 2020

Python Setup Cheat Sheet

Python is an interpreted high-level and general purpose programming language. Python 2.0 was released in 2000 but officially discontinued in 2020. Python 3.x is now the preferred version for most projects e.g. v3.7.

Python is commonly used in artificial intelligence and machine learning projects with the help of libraries like TensorFlow, Keras, Pytorch and Scikit-learn. There is also open source Python distribution Anaconda used for data science and machine learning applications that aims to simplify package management and deployment.

Let's check it out!


Install Python
Install Python on Windows, Mac OS/X and Linux. Install pip as the de facto standard package-management system. Install Anaconda for future AI projects. Finally, install + configure an IDE for Python programming.

Windows
Follow instructions here how to install Python 3 on Windows. Download latest version of Python for Windows including IDLE pip + documentation. Add Python to PATH variable making it easier to configure your system.

Mac OS/X
Python is installed by default on most Mac OS/X systems. Launch terminal + type python and pip to confirm.

Linux
Python is installed by default on most Linux systems. Launch terminal + type python. However you may also need to install pip. Update your ~/.bashrc file as necessary to use Python 3.7 by default instead of Python 2.
 Install + Update + Aliases Python3  Ubuntu Linux unable to locate package python-pip
 sudo apt install python3-pip
 python -m pip install --upgrade pip
 echo "alias python=python3" >> ~/.bashrc
 echo "alias pip=pip3" >> ~/.bashrc
 sudo apt-get install software-properties-common
 sudo apt-add-repository universe
 sudo apt-get update
 sudo apt-get install python3-pip

IMPORTANT
Verify which python version and which pip version are installed and location where these are both installed:
 python --version  which python
 pip --version  which pip


Install Anaconda
Follow instructions to install open source Python distribution Anaconda for Windows and Mac OS/X and Linux
 SYSTEM  LOCATION
 Windows  %USERPROFILE%\Anaconda3
 Mac OS/X  /anaconda3
 Linux  /anaconda3

Windows
Add the following four environment variables: System | Advanced system settings | Environment Variables:
 %USERPROFILE%\Anaconda3  %USERPROFILE%\Anaconda3\libs
 %USERPROFILE%\Anaconda3\Scripts  %USERPROFILE%\Anaconda3\Library\bin

Mac OS/X
Follow all prompts from the install wizard. Anaconda should install at /anaconda3 by default with aliases set.

Linux
Launch terminal as root user. Type bash ~/Downloads/Anaconda3-2020.02-Linux-x86_64.sh. For consistency with the Mac set install location to /anaconda3. After install launch Anaconda navigator. Enter the following:
 source ~/anaconda*/bin/activate root
 anaconda-navigator

Update your ~/.bashrc file as necessary to prefer Anaconda Python especially for data science AI + ML work.
 alias python=/anaconda3/bin/python
 alias pip=/anaconda3/bin/pip

Finally, create Anaconda desktop shortcut: create the following Anaconda.desktop at /usr/share/applications/ Add the text below. Enter sudo echo "PATH=$PATH:/anaconda3/bin" >> /etc/environment at the terminal.

Anaconda.desktop
[Desktop Entry]
Type=Application
Name=Anaconda
Exec=anaconda-navigator
Terminal=false
Icon=/anaconda3/lib/python3.7/site-packages/anaconda_navigator/static/images/anaconda-icon-256x256.png
Restart Linux. From settings type "Anaconda" to prompt Anaconda Navigator + resume projects from there.

IMPORTANT
Verify which Anaconda version is installed using conda --version command and where installed where conda.


Install PyCharm
PyCharm is an integrated development environment used specifically for Python. Install PyCharm during the Anaconda install or from navigator. Otherwise install PyCharm for Windows and Mac OS/X and Linux directly.

Launch PyCharm. Click "Configure" cog drop down | Settings. Set the base interpreter to match the Python location for Anaconda. Click cog | Show All | Click "+" | Virtualenv Environment. Enter the following details:

 SYSTEM  LOCATION
 Windows  %USERPROFILE%\Anaconda3\python.exe
 Mac OS/X  /anaconda3/bin/python
 Linux  /anaconda3/bin/python

Plugins
Configure cog | Plugins. Install pylint as a code analysis tool that follows the recommended PEP8 style guide. Restart IDE. Pylint is handy tool to check Python code especially for developers used to static code compiler!

Shortcuts
Track active item in solution explorer similar to Visual Studio: Click cog to right of Project and Always Select Opened File. Hit shift key twice for quick search. Remember Rename files and folder is in the Refactor menu!
 Ctrl + F12  Prompt popup to list all the methods in the class
 Ctrl + Shift + N  Prompt popup to search for text found in all files
 Ctrl + Shift + I  View definition of function or method in the class
 Ctrl + Click function  Navigate to function definition or method in class
 Ctrl + Alt + Left arrow  Navigate backwards to previous location
 Ctrl + Alt + Right arrow  Navigate forwards to the next location

IMPORTANT
On Linux navigate may actually be Shift + Alt + arrow keys instead of Ctrl due to keymap shortcut conflicts!


Hello PyCharm
Launch PyCharm. Create New Project | "HelloPyCharm". Enter the following details as Anaconda interpreter:

PyCharm uses virtualenv tool to create an isolated Python environment. Virtualenv creates venv folder in the current directory with Python executable files and a copy of pip to install other modules and packages.

Create simple "HelloWorld.py". Enter Python code print('Hello World'). Right click file | Run ""HelloWorld".

Module Not Found
Update script | Import module not currently installed e.g. import numpy. Run script: ModuleNotFoundError


Launch PyCharm terminal. Enter python -m pip install numpy to install module. Re-run script with success.


Alternatively, execute pip freeze to dump all required modules for project into requirements.txt and install:
 pip freeze > requirements.txt
 pip install -r requirements.txt


Code Sample
Let's test drive develop a simple code sample as a Python module that could be deployed as Python package.

IMPORTANT
A module is a single Python script file whereas a package is a collection of modules. A package is a directory of Python modules containing an additional __init__.py file to distinguish from a directory of Python scripts.

Launch PyCharm. Create New Project | "PackagePyCharm". Configure Python Anaconda interpreter above. Right click PackagePyCharm project | New | Python Package | "MyPackage". Create other top level folders.

Create sub folders src and tests beneath "MyPackage". Create requirements.txt file + setup.py beneath "MyPackage" also. Finally, create module.py and __init__.py under src and test_module.py under tests.

 test_module.py  module.py
 import unittest
 from MyPackage.src.module import add_one

 class TestSimple(unittest.TestCase):

    def test_add_one(self):
        result = add_one(5)
        self.assertEqual(result, 6)

 if __name__ == '__main__':
    unittest.main()
 def add_one(number):
    return number + 1

Right click inside test_module.py | Debug 'Unittests for test_module.py'. Alternatively, Terminal command:

python -m unittest discover MyPackage

IMPORTANT
If the Terminal reveals Run 0 tests then ensure package has __init__.py setup in every relevant sub folder. Finally, enter dependencies for requirements.txt file and setup.py e.g. numpy. Install package at terminal:
 setup.py  requirements.txt
 import setuptools

 with open("README.md", "r") as fh:
    LONG_DESCRIPTION = fh.read()

 setuptools.setup(
    name='MyPackage',
    version='0.1.2',
    description="My test package.",
    long_description=LONG_DESCRIPTION,
    long_description_content_type="text/markdown",
    packages=setuptools.find_packages(),
    install_requires=[
        "numpy>=1.17.1",
    ]
 )
 numpy>=1.17.1

pip install MyPackage/.

Finally, we could also replicate the unit test code directly on the REPL. Select Terminal tab. Enter commands:

 python
 >>> from MyPackage.src.module import add_one
 >>> result = add_one(5)
 >>> print(result)

Code Linting
Linting highlights syntactical and stylistic problems in Python source code which helps identify and correct subtle programming errors. In PyCharm, choose Pylint tab and click Play button to list any errors in code.

Alternatively, select Terminal and type specific linter like flake8 to check Python source code against PEP8 coding style programming errors. If flake8 is not installed then simply type pip install flake8 at Terminal.


Summary
To summarize, we have a simple setup for Python programming on Windows, Mac OS/X and Linux. There is much to explore e.g. Python 3.8. However, we'd like to setup Python more for AI machine learning projects. This will be topic of the next post.

Tuesday, June 30, 2020

Candy Kid Code Complete

Candy Kid is a simple maze chase video game originally programmed by Grandstand Leisure from New Zealand in September 1984. The game was written in BASIC programming language on Sega SC-3000.

In 2015, Candy Kid was re-written in C#/.NET and XNA for Windows PC and ported to iOS / Android using MonoGame. After finding the original source code on SMS Power!, Candy Kid was re-written again in 8-bit.

Inspired from previous posts on Sega Console Programming and devkitSMS Programming Setup + Sample, Candy Kid is my fifth 8-bit video game written in C / Z80 assembler to target Sega Master System (SMS).

Let's check it out!

Note: previous titles published for the Master System include 3D City, Simpsons Trivia + Platform Explorer.
Download source code here.

Sega Retro Gaming post documents how to play video games like Candy Kid SMS using the Fusion emulator.
devkitSMS Programming Setup post documents development environment required to build C / Z80 source.
Instructions
Eat all the candy to pass each level. Eat all bonuses to maximize your score! The 3x Candy "Mama" enemies Pro / Adi / Suz have different passive and aggressive personalities and alternate between scatter and attack.

Tools
Here are a list of Tools and frameworks that were used in the development of this project:
 KEY  VALUE
 Programming  devkitSMS
 Compiler  sdcc 3.6
 Assembler  WLA-DX
 IDE  Visual Studio 2015
 Languages  C / Z80
 Graphics  BMP2Tile 0.43 / GIMP 2 / paint.net
 Music  Mod2PSG2 / vgm2psg
 Emulators  Emulicious / Fusion / Meka

ROM Hacking
You can hack this ROM! Download + dump CandyKid into Hex Editor, e.g. HxD, and modify bytes:
 ADDRESS  VARIABLE  DESCRIPTION
 0x004F  Debugger  Used to show debugging info for game.
 0x0050  Invincible  Non-zero value enables invincibility.
 0x0051  FullBoost  Non-zero value enables maximum boost.
 0x0052  Trees Type  Set value to 1=Show otherwise 2=Kill.
 0x0053  Exits Type  Set value to 1=Open otherwise 2=Shut.
 0x0054  Difficulty  Set value to 1=Easy otherwise 2=Hard.
 0x0055  Game Speed  Set value to 1=Slow otherwise 2=Fast.
 0x0056  World No.  Set start World no currently 1 to 10.
 0x0057  Round No.  Set start Round no currently 1 to 10.
 0x0058  Music Off  Set 0=Music to play otherwise silent.
 0x0059  Sound Off  Set 0=Sound to play otherwise silent.

Bonuses
  • There are 4x different bonuses: 100 / 200 / 400 / 800 pts. Double bonus points after level 70.
  • Player will receive extra 2,000 pts after eating all the candy and collecting all bonuses in level.

Cheats
  • Press button 2 five times during Title screen and you'll be invincible each game this is actioned.
  • Press and hold button 2 during game play to action Game Over and force quit out of the game.
  • Press and hold button 2 on Splash screen to reset High score and all options previously saved.

Credits
Extra special thanks goes to sverx for the devkitSMS. Plus StevePro Studios would like to thank eruiz00 and haroldoop for sharing source code from SMS Homebrew projects. Many ideas here were used in this project!

Promotion
Thanks to SMS Power! for making this video. Here is the official Candy Kid Facebook page and Blogger label.

Summary
To summarize, Candy Kid has had a very interesting journey after first appearing on the Sega SC-3000 in 1984 to being ported to Windows PC, iOS + Android in 2015 and finally the Sega Master System in 2020.

In fact, after porting the original game in 2015 the ultimate goal was always to eventually back port Candy Kid to the Master System. Thanks to the awesome Indie game dev scene this goal has now been achieved!

Sunday, May 31, 2020

devkitSMS Programming Sample IV

In 2017, we checked out devkitsms to setup productive development environment to build 8-bit retro video games for the Sega Master System (SMS). In 2019, we improved build process to develop Platform Explorer.

Though after five years of SMS development we would like to connect more the C language with underlying Z80 assembly code. Look for opportunities to consolidate 8-bit retro game development to build Candy Kid!
Let's check it out!

Game
Candy Kid video game was originally programmed by Grandstand Leisure from New Zealand in September 1984. The game written in BASIC on the Sega SC-3000. Thanks to the awesome SMS Power! community Candy Kid is available on Sega Master System in 2020 Coding Competition. Download source code here.

Software
Follow all instructions from the previous post: this documents how to setup the pre-requisite software.
Note: ensure you have downloaded and installed the devkitSMS and Small Device C Compiler [SDCC].

Checklist
Here is a checklist of tasks to complete in order to try and improve the existing development environment:
  • Connect more the C language and Z80 assembly code via emulation
  • Implement various code architectural updates from the devkitSMS
  • Apply techniques to resolve vulnerabilities debugging source code
  • Explore strategies to confront the 32KB ROM game code capacity

Emulation
In 2013, we checked out Sega Console Programming with goal to build retro game in Z80 assembler for the Sega Master System. However, the process to write Z80 assembly language from scratch seemed daunting.

In 2019, since collaborating with Calindro, we now have the opportunity to understand relationship between C source code and the generated Z80 assembly: Emulicious will decorate Z80 code in the debugger with its corresponding C function!

Launch Emulicious. Tools menu | ensure "Reopen Tools" option is checked. Launch the debugger. File menu | ensure "Load Sources automatically" is checked + Run menu | ensure "Suspend On Open" is checked. Close
Ensure the output.map file is not deleted in build script. E.g. both output.sms and output.map are built and loaded into Emulicious. The output.map maps C source code functions to the underlying Z80 assembly code.

Example
Search for main function. Ctrl + F | Find "_main" Ctrl+click "main" to navigate there. Ctrl+click any function name to navigate + inspect Z80 assembly code built for that function. Click Alt+Left to navigate back. Nice!

Coding
The following coding changes and enhancements have been made throughout Candy Kid development cycle:

Collision
Leverage the built-in SMS_VDPFlags for fast sprite collision detection. If value equals 0x20 then at least two sprites have collided then implement slow(er) collision detection by checking if sprite rectangle(s) intersect:

play_screen.c
gamer_collision = devkit_isCollisionDetected();
if ( 0 != gamer_collision )             // Fast.
{                                       // Slow.
    for( enemy = 0; enemy < MAX_ENEMIES; enemy++ )
    {
        eo = &global_enemy_objects[ enemy ];
        if( go->posnX > eo->posnX )     { dx = go->posnX - eo->posnX; }
        else                            { dx = eo->posnX - go->posnX; }

        if( go->posnY > eo->posnY )     { dy = go->posnY - eo->posnY; }
        else                            { dy = eo->posnY - go->posnY; }

        if( dx <= distance && dy <= distance ) { gamer_collision = enemy; break; }
    }
}

Levels
In 2018, Duckslayer Adventures by haroldoop gave a great example of how levels could be stored externally in text files then converted into byte arrays using folder2c. The byte arrays are then interpreted for drawing:
 KEY  VALUE
 0  Blank
 1  Trees
 2  Candy
 3  BonusA
 4  BonusB
 5  BonusC
 6  BonusD
  // Level0101
  3000000004
  0002222000
  0000000000
  0211111120
  0210110120
  0210110120
  0211111120
  0000000000
  0002222000
  5000000006

SRAM
Persisting important data like Hi Scores and player settings across game sessions makes for better gaming experience. Here is the Candy Kid implementation with SRAM support currently included in the devkitSMS:

storage_manager.c
#define MAGIC   0xACE0B004
#define FINAL   0xFF

// Global variable.
struct_storage_object global_savegame_object;

unsigned char engine_storage_manager_available()
{
    struct_storage_object *savegame = ( struct_storage_object* ) ( devkit_SMS_SRAM() );
    unsigned char foundMagic;

    devkit_SMS_enableSRAM();
    foundMagic = MAGIC == savegame->Magic;
    devkit_SMS_disableSRAM();
    return foundMagic;
}
void engine_storage_manager_read()
{
    struct_storage_object *savegame = ( struct_storage_object* ) ( devkit_SMS_SRAM() );
    struct_state_object *st = &global_state_object;

    devkit_SMS_enableSRAM();
    st->state_object_high_score = savegame->save_high_score;
//  ...
    devkit_SMS_disableSRAM();
}
void engine_storage_manager_write()
{
    struct_storage_object *savegame = ( struct_storage_object* ) ( devkit_SMS_SRAM() );
    struct_state_object *st = &global_state_object;

    devkit_SMS_enableSRAM();
    savegame->Magic = MAGIC;
    savegame->save_high_score = st->state_object_high_score;
//  ...
    savegame->terminal = FINAL;
    devkit_SMS_disableSRAM();
}
void engine_storage_manager_erase()
{
    struct_storage_object *savegame = ( struct_storage_object* ) ( devkit_SMS_SRAM() );

    devkit_SMS_enableSRAM();
    savegame->Magic = 0x00000000;
    savegame->save_high_score = DEF_HI_SCORE;
//  ...
    savegame->terminal = FINAL;
    devkit_SMS_disableSRAM();
}

Debugging
Buffer overflow [or overrun] is an anomaly where writing data to buffer overruns the buffer's boundary and overwrites adjacent memory. In Candy Kid, the player can be killed by any one of the three Mama enemies; Actors 0-2. But Kid can also be killed by death tree ie "actor" 4. When Kid is killed we initially reset the "kill" enemy unconditionally. Thus if killed by death tree enemy "4" would be reset hence a buffer overrun occurs! Big thanks to Calindro + sverx for helping debug this issue. The solution is to check enemy kill before reset.


Capacity
The following challenges were encountered with 32KB ROM game code capacity during development cycle:

Input
The previous input manager had clean interface that's "Easy to use correctly" and "Hard to use incorrectly" But this suffered from code bloat + additional costly bytes. Therefore, inject key test for simpler interface:

input_manager.c
#include "input_manager.h"
#include "..\devkit\_sms_manager.h"

static unsigned int curr_joypad1 = 0;
static unsigned int prev_joypad1 = 0;

// Public methods.
void engine_input_manager_update()
{
    prev_joypad1 = curr_joypad1;
    curr_joypad1 = devkit_SMS_getKeysStatus();
}
unsigned char engine_input_manager_hold( unsigned char data )
{
    return curr_joypad1 & data && !( prev_joypad1 & data );
}
unsigned char engine_input_manager_move( unsigned char data )
{
    return curr_joypad1 & data;
}

Banks
In 2017, Astro Force by eruiz00 gave great example on how to store static data in fixedbank.c to scale out more game content and bank switch on-the-fly without consuming the precious 32KB ROM for game code.

Candy Kid attempts to replicate this strategy, unfortunately still hitting 32KB capacity for game code. Next step could be store function pointer arrays in fixedbank.c like Astro Force to conserve more precious bytes!

fixedbank.c
const unsigned char bossX_object_speed[] = { 1, 1, 2, 1, 1, 1, 3, 3, 1, 1, 1, 1 };
const unsigned char bossX_object_delay[] = { 2, 2, 3, 1, 1, 1, 2, 2, 3, 3, 2, 2 };
const unsigned char enemy_object_image[] = { 0, 2, 4, 12, 14, 16, 24, 26, 28, 36, 38, 40 };
const unsigned char enemy_object_speed[] = { 2, 3, 1, 3, 2, 1, 3, 2, 1, 1, 2, 3 };
const unsigned char enemy_object_delay[] = { 3, 3, 1, 2, 3, 1, 2, 1, 2, 2, 3, 3 };
const unsigned char enemy_object_hands[] = { 90, 80, 70, 60, 95, 85, 75, 65, 99, 90, 80, 70 };
const unsigned char enemy_object_waits[] = { 80, 60, 40, 20, 85, 65, 45, 25, 90, 70, 50, 30 };
const unsigned char enemy_object_tours[] = { 64, 72, 80, 88, 68, 76, 84, 92, 72, 80, 88, 96 };
const unsigned char enemy_object_kills[] = { 65, 73, 81, 89, 69, 77, 85, 93, 73, 81, 89, 97 };
const unsigned char gamer_object_image[] = { 0, 2, 4, 12, 14, 16 };
const unsigned char gamer_object_speed[] = { 1, 2, 3, 3 };
const unsigned char gamer_object_delay[] = { 1, 1, 2, 1 };
// TODO potentially include function pointer arrays here also!

Build
In previous projects our build.bat file had always used SDCC compiler switch to favor code speed. However, as Candy Kid was struggling to stay within 32KB ROM limit, unfortunately was necessary to favor code size:

build.bat
sdcc -c -mz80 --opt-code-size --peep-file peep-rules.txt --std-c99 main.c

Command
In 2009, we implemented the Command Design pattern to record sprite movement in Henway to playback for debugging and demo modes. Therefore, we would also like to implement the Command Design pattern for Candy Kid. The motivation here is record enemy movement for Reinforcement Learning in video games.

command_manager.h
void engine_command_manager_add( unsigned int frame, unsigned char command_type, unsigned char args );
void engine_command_manager_execute( unsigned int frame );
play_screen.c
void screen_play_screen_update( unsigned char *screen_type )
{
    // Implement logic to boost and move Candy Kid gamer.
    engine_command_manager_add( frame, command_type_gamer_speed, gamer_boost );
    engine_command_manager_add( frame, command_type_gamer_mover, gamer_direction );

    // Implement logic to boost and move Candy Mama enemies.
    engine_command_manager_add( frame, command_type_enemy_speed, ( enemy | ( enemy_boost << 4 ) ) );
    engine_command_manager_add( frame, command_type_enemy_mover, ( enemy | ( enemy_direction << 4 ) ) );

    // Execute all commands for this frame.
    engine_command_manager_execute( frame );
}
Rather than boost and move actors directly from game events we add these as commands to an array and execute them accordingly. However, even when storing 255 commands this quickly added to 1KB memory!

command_manager.c
#define MAX_FRAMES 255
static unsigned int command_object_frames[ MAX_FRAMES ];
static unsigned char command_object_cmds[ MAX_FRAMES ];
static unsigned char command_object_args[ MAX_FRAMES ];
Unfortunately, due to competition time constraints and hitting 32KB capacity for game code the Command Design pattern was not fully implemented. Next steps: code refactor and perhaps storing less commands.


z88dk
In 2016, 3D City was built using an old version of the z88dk which worked on emulators but real Master System hardware experienced graphics glitches. Subsequently, the updated version of z88dk integrates devkitSMS which also works on real hardware. In fact, this is how Galactic Revenge was built by eruiz00. This was preferred because the libraries are optimized in assembly language and produce smaller object code.

Therefore, this seemed a promising option for Candy Kid especially late in the development cycle when the game hit 32KB capacity for game code. Unfortunately, z88dk does not support the following two APIs used:
 SMSlib.h  extern volatile unsigned char SMS_VDPFlags;
 PSGlib.h  void PSGResume (void);


Summary
To summarize, the development process to build Candy Kid, like all previous SMS projects, has been very educational + has provided numerous opportunities to collaborate more with the SMS Power! community.

However, the biggest challenge in completing Candy Kid was to stay within the 32KB ROM game code limit. Here is a short list of current + future options:
  • Build game code in Z80 assembly directly for complete control of the machine code byte output
  • Refactor duplicate code to store generic code in function pointer arrays accessible by fixed bank
  • Integrate z88dk and devkitSMS that produces smaller object code by using only supported APIs
  • Upgrade from 8-bit Master System to 16-bit Mega Drive and bypass 32KB hard limit altogether

In fact, the final option is the most unique: much like how the Sega Master System has the devkitSMS to support homebrew game development using C language, the Sega Mega Drive has the SGDK. Therefore, would be interesting to transition many Sega Retro Gaming ideas developed from 8-bit onward to 16-bit!

Wednesday, January 1, 2020

Retrospective XI

Last year, I conducted a simple retrospective for 2018. Therefore, here is a retrospective for 2019.

2019 Achievements
  • Streamline Sega Master System retro game development using the devkitSMS
  • Win the Sega Master System annual coding competition with Platform Explorer
  • Apply devkitSMS programming skills to Mega Drive prototypes using the SGDK
  • Upgrade MonoGame version cross platform for future 3D graphics and shaders
  • Complete Linux installation process using virtual machine on Windows and Mac
  • Integrate new scripting languages including Python, Powershell and Terrarform
  • Expand cloud computing experience: Amazon Web services and Azure DevOps
  • Learn from Data Scientists and Machine Learning Engineers strategies at work
Note: winning the Sega Master System annual coding competition is a unique achievement!

2020 Objectives
  • Setup Sega dev + debugging environment for MegaDrive similar to Master System
  • Build MonoGame project cross platform containing 3D models and custom shaders
  • Integrate new software deployment techniques for containerization + orchestration
  • Continue AI and Machine Learning application to personal and professional projects

Personal
Earlier in 2019, I gave presentation on Game Development. I believe this provides excellent experience for Software Engineering in general because algorithms must be written efficiently to achieve consistent frame rates. Therefore, after working with Sega Master System would now like to experiment with the Mega Drive!


Professional
20 Predictions about Software Development trends in 2020 include: Infrastructure as all roads lead to Cloud, Containerization [Docker] + Orchestration [Kubernetes] and Microservices as REST APIs will be mainstream.

However, the big buzz continues to be centered around: Artificial Intelligence, Machine Learning and how can Big Data be leveraged by companies to improve their customer experiences and gain competitive advantage.


Future
After giving the presentation earlier in 2019, I was advised afterwards about a new interesting development connecting Artificial Intelligence and Video Games now described as Reinforcement Learning in Video Games.

Therefore, this seems excellent motivation to investigate Machine Learning Software such as TensorFlow for various applications of ML such as Reinforcement Learning and see if this can be applied to Video Games J

Friday, November 15, 2019

Mobile Video Game Updates

In 2015, we built Candy Kid as a simple maze chase video game on Windows PC using an older version of MonoGame published on Android and iOS. In 2018, we built 3D City "Shoot 'em up" using similar process.

Now both Google and Apple require apps + games be 64-bit compliant. Apple will stop support for 32-bit on iOS 11.0. Plus, Google require updates on Google Play to target minimum Android 9 (API level 28) or higher.

All these factors mandate upgrades to our mobile games to be rebuilt using latest version of MonoGame 3.7.
Let's check it out!

TL;DR
Too Long; Didn't Read
  1. Install all pre-requisite software on Windows + Mac. Check for updates to get latest dependencies
  2. Launch Android + iOS projects. Update all settings to increment build version numbers and codes
  3. Rebuild + upload the updated binaries to relevant online stores then tag all archived source code

Pre-Requisites
Reference previous instructions for Android and iOS ports. This post assumes software already published. Install the following software on Windows and Mac. Install "Mobile development with .NET" for Xamarin.
  Windows   [Android]   Mac Os/X   [iOS]
Note: Check for updates on all Visual Studio IDEs to get latest the frameworks tools and dependencies.

IMPORTANT
If, for any reason, there are issues updating any IDEs mentioned here then simply Run as Administrator.

Pipeline
Previous posts used older versions of MonoGame which meant the Content Pipeline was arguably not built correctly. Upgrade to MonoGame 3.7 and build the Content Pipeline correctly using the Content.mgcb file.

Launch the MonoGame Pipeline tool. Navigate to project Content folder. Open Content.mgcb. Click Content root node. Under Properties select relevant Platform. Create following folders and add content accordingly:

Data
Import any ASCII text files or XML configuration files. Set the Build Action in the Properties section to Copy.

Fonts
Typically import pre-built XNB binary files from previous games e.g. Emulogic.xnb. Set Build Action to Copy. However, if you create custom spritefont files copy the TTF file also otherwise "Could not find font file" error.

Sound
Import all music and sound effects as MP3. Some WAV files sounded strange as built sound effects. Convert WAV files to MP3. Set Build Action to Build. Set Processor to Song for music otherwise set to Sound Effect.

Textures
Import all 2D images as PNG. Some JPG files rendered strange as built textures. Convert JPG files to PNG. Also, convert BMP files to PNG. Set Build Action to Build. Set Processor to Texture for all imported images.


Google
On November 1, 2019 apps + games on Google Play required to target Android 9 (API level 28) or higher. After this date, Google Play Console will prevent any APK updates submitted with targetSdkVersion < 28.

Therefore, install Android 9 (API level 28) or higher. Launch Android Studio. File | Settings... | Appearance and Behavior | System Settings | Android SDK. Note default location may be different from Visual Studio:
 Android Studio  %USERPROFILE%\AppData\Local\Android\Sdk
 Visual Studio [VS]  C:\Program Files (x86)\Android\android-sdk

Actually, it may be easier to align Android SDK location in Android Studio to that of Visual Studio because all Android game code will be built using MonoGame from Visual Studio. Therefore, update Android SDK path:
Ensure SDK Tools are also installed. Verify SDK Platforms installed in "platforms" sub-folder and SDK Tools installed in "build-tools". Finally, add "platform-tools" folder to System %PATH% to use adb at cmd prompt.

Upgrade
Currently VS2019 does not include MonoGame project templates. Therefore, launch VS2017 and create new MonoGame Android project e.g. 3D City. Close and re-open in VS2019. The benefits to be explained shortly.

Assume previous version 3D City 1.0.0 already exists on Google Play. Import existing content per previous build under the Content folder as explained above. Import existing code. Right click project and Properties:

Application
Compile using Android version (Target Framework). Choose Android 9.0 (Pie) as minimum Android version.

Application Manifest
 Key  Value  Element  Attribute
 Application name  3D City  application  android:label
 Package name  com.steveproxna.x3dcity  manifest  package
 Application icon  @drawable/Icon  application  android:icon
 Version number  2  manifest  android:versionCode
 Version name  1.1.0  manifest  android:versionName
 Minimum Android version  Android 9.0 (API Level 28 - Pie)  uses-sdk  android:minSdkVersion
 Target Android version  Android 9.0 (API Level 28 - Pie)  uses-sdk  android:targetSdkVersion

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.steveproxna.x3dcity" android:versionCode="2" android:versionName="1.1.0">
  <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /">
  <application android:label="3D City" android:icon="@drawable/Icon">
    <meta-data android:name="android.max_aspect" android:value="2.1" />
  </application>
</manifest>

Application Options
Click the Advanced button. Target the following 2x Supported architectures: armeabi-v7a and arm64-v8a.

Currenlty, Android 10.0 (Q) is available although this API level 29 seems to be missing from Visual Studio Tools menu | Android | Android SDK Manager. However, here is a workaround to circumvent the problem:

Launch Android Studio. Install Android 10 (API level 29) as above. Ensure android-29 exists in Android SDK "platforms" folder and 29.0.2 in "build-tools". Launch Visual Studio 2019. Update Android version in project:

Upload
Launch VS2019. Open MonoGame Android project. Attach Android device. Select Release build configuration. Clean and rebuild solution. Right click project | Archive... After the archive is generated choose Distribute...

Ad Hoc
Select distribution channel: Ad Hoc. Select Signing Identity or Create Android Keystore. Save APK. Enter the password. Open Folder. Navigate to "signed-apks" subfolder. Here is the signed APK that can be installed:
# Start | run | cmd
# cd signed-apks
# adb devices
adb install -f *.apk

Google Play
Select distribution channel: Google Play. Select Signing Identity. Continue. Click "+". Login to Google Play | Settings | API access. Select existing OAuth client. Download JSON and extract the Client ID + Secret here:

TROUBLESHOOTING
If you receive any of the following 403 errors while attempting to upload latest Android build then read on:

This release is not compliant with the Google Play 64-bit requirement
This can occur if you have not selected arm64-v8a in the Android project properties Application Options. If after selecting arm64-v8a you receive NoAudioHardwareException then must upgrade MonoGame to 3.7.1.

The apk must be signed with the same certificates as the previous version
This can occur when you have signed previous Android build using keystore typically on different computer. Export Android keystore as needed and ensure critical information like keystore alias and password are safe.

Here is a good overall guide for finding the Android keystore. Below is quick summary for all configurations:
 Windows  %USERPROFILE%\AppData\Local\Xamarin\Mono for Android\Keystore\alias\alias.keystore
 Mac OS/X   $HOME/Library/Developer/Xamarin/Keystore/alias/alias.keystore

Don’t forget to backup your keystore file! Here is how to retrieve the alias and do not forget the password:
# Search for Alias name in the output from the following command
cd %USERPROFILE%\AppData\Local\Xamarin\Mono for Android\Keystore\alias
keytool -list -v -keystore alias.keystore


Apple
Apple will stop support for 32-bit apps on iOS 11.0. Therefore, any 32-bit apps will need to be upgraded to 64-bit. Also, Apple may require paid apps be upgraded within every two years to remain on the App Store.

Sign in developer.apple.com. Ensure all developer provisioning profiles use certificates for Xcode 11 or later:

Upgrade
Currently VS2019 does not include MonoGame project templates. Therefore, launch VS2017 and create new MonoGame iOS project e.g. 3D City. However, do NOT click "Pair to Mac" here because you may be prompted to install Mono version incompatible with this version of Visual Studio. Instead, close and re-open in VS2019.

Assume previous version 3D City 1.0.0 already exists on the App Store. Import existing content per previous build under the Content folder as explained above. Import existing code. Right click project and Properties:

Info.plist
 CFBundleName  3D City
 CFBundleDisplayName  3D City
 CFBundleIdentifier  com.steveproxna.3dcity
 CFBundleVersion  1.1.0
 CFBundleShortVersionString  1.1
 MinimumOSVersion  7.0
 UISupportedInterfaceOrientations  UIInterfaceOrientationLandscapeLeft
 UIStatusBarHidden  true
 UIRequiresFullScreen  true
 UIDeviceFamily  1, 2
 XSAppIconAssets  Assets.xcassets/AppIcon.appiconset

IMPORTANT
Currently there seems to be inconsistency with AppIcon assets creation on Windows and Mac. Therefore, launch MonoGame iOS project in Visual Studio for Mac. Open Info.plist | Click "Use Asset Catalog" here.

This action creates the AppIcon.appiconset under Assets.xcassets folder whereas Windows creates under Media.xcassets which means App icon may not appear on iOS device! Also, ensure Content.json is correct.


Upload
Launch Visual Studio for Mac. Open MonoGame iOS project. Attach iOS device. Select Release configuration. Clean and rebuild solution. Right click project | Archive for Publishing. After the archive is generated choose Sign and Distribute...

Ad Hoc
Select distribution channel: Ad Hoc. Choose provisioning profile | Publish | Save IPA file | Reveal in Finder. Launch Xcode | Window | Devices + Simulators. Select iOS device. Click "+". Navigate and open IPA file.


App Store
Launch browser. Sign in to iTunes Connect. Select published app e.g. 3D City. Click "+" Version or Platform. Choose iOS. Enter new Store Version Number | Create. Enter "What's New in this Version" text. Save data.

Sign in Apple ID web site. In Security section under "APP-SPECIFIC PASSWORDS" click "Generate Password". In the popup window enter, for example "3DCity" | Create. Apple will generate a new app specific password.

Select distribution channel: App Store | Upload. Choose provisioning profile. Enter Apple ID and App Specific Password. Next | Publish. Choose file name | Save IPA. This process should upload build to iTunes Connect.

Finally once build has completed processing select it in iTunes Connect. Save. Complete following questions:
 Export Compliance
 Have you added or made changes to encryption features since your last submission of this app?

 NO 
 Advertising Identifier
 Does this app use the Advertising Identifier (IDFA)?

 NO 

IMPORTANT
If you receive Publishing Failed error "Invalid Toolchain: You app was built with unsupported version of Xcode or SDK" then you must upgrade to Xcode 11.2.1 because Apple deprecated Xcode 11.2 after Nov 5th 2019.

Right click MonoGame iOS project | Archive... | App Store | Save IPA file. Upload on Mac Application Loader:

Also, if you receive Publishing Failed error "Failed to parse PList data type" then simply increment the build version number CFBundleVersion in Info.plist. Align project properties. Rebuild and upload to iTunes again.


TROUBLESHOOTING
If you receive any of the following crashes while attempting to run latest iOS build on device then read on:

Got a SIGABRT while executing native code. This usually indicates a fatal error in the mono runtime used
For some strange reason the following code with IDictionary<Byte, T> worked previously but now crashes. Fortunately, there is a workaround to this issue: simply use the Int16 type as the key instead of type Byte.
// This code will crash on iOS device!
private IDictionary<Byte, Vector2> BasePositions { get; private set; }
private IDictionary<Byte, Rectangle> BasePositions { get; private set; }

// This code will work without crash.
private IDictionary<Int16, Vector2> BasePositions { get; private set; }
private IDictionary<Int16, Rectangle> BasePositions { get; private set; }

Microsoft.Xna.Framework.Content.ContentLoadException: 'The content file was not found.'
This general exception can occur when MonoGame project has not built the Content as described above i.e. New MonoGame project using version 3.7. Open Content.mgcb. Add all content relative to this content file.

Also, I believe it helps to fully qualify Content path and remove default code to set Content RootDirectory:
// Delete or comment out default code.
// Content.RootDirectory = "Content";
// Fully qualify path to load content e.g. Image.png
Texture2D image = Content.Load("Content/Textures/Image");

Summary
To summarize, all games built in the past using MonoGame were 2D. However, now that all frameworks tools and dependencies are upgraded with latest MonoGame installed using correct build pipeline then we are in a good position to assemble 3D graphics and games on Windows deployed to Android and iOS cross platform!