Skip to content
Menu
Adam Whitney
  • About
  • Privacy Policy
  • Radio
Adam Whitney
October 1, 2023July 4, 2024

Commodore 64 As A Service

Introduction

In seeking a lightweight framework to implement a REST API as an application back-end, Flask quickly rose to the top of my list. Flask is a strong choice for creating an API, and it also fit my secondary goal of letting me flex my Python skills, a language that up until now I had only used for some data analysis projects.

Flask is “a lightweight WSGI web application framework. It is designed to make getting started quick and easy, with the ability to scale up to complex applications. [Flask] has become one of the most popular Python web application frameworks.” This was speaking my language: simple, lightweight, standards-based, and the ability to scale up. Simplicity alone was very important to me, as I began to bulk up my skill and confidence in Python.

In addition to using Flask, I wanted to embrace the “API first” design principle and ideally adopt the OpenAPI specification. I found a great tutorial for doing just this in Flask, and using Connexion which is built just for this purpose!

With the frameworks selected and a great tutorial as a guide, the only remaining thing needed was a proof-of-concept as a first creation. Let’s have a bit of fun. Let’s build Commodore 64 as a Service.

OpenAPI and Connexion

OpenAPI allows you to define your REST API in a YAML or JSON file. The definition includes at minimum the supported API endpoints and operations, operation parameters, response output, and any authentication. Below is an excerpt of the OpenAPI definition for C64aaS.

paths:
  /program:
    get:
      operationId: "program.read_all"
      summary: "LIST the entire program."
      responses:
        "200":
          description: "Successfully read program LISTing."
    post:
      operationId: "program.create"
      summary: "Create a NEW program."
      requestBody:
          description: "Program to create"
          required: True
          content:
            application/json:
              schema:
                x-body-name: "program"
                $ref: "#/components/schemas/ProgramList"
      responses:
        "201":
          description: "Successfully created program."

Connexion will read the OpenAPI specification (in swagger.yml) and handle the HTTP requests, routing to the correct Python function to handle them, and sending the response.

import connexion
app = connexion.App(__name__, specification_dir="./")
app.add_api("swagger.yml")
@app.route("/")
...
app.run(host="0.0.0.0", port=6464, debug=True)

Connexion uses the operationId parameter for each path to determine which Python function should handle the request. For example, on a POST operation to the /program URL, the create method will be called within the program.py module. The allows you to be modular with the code supporting each endpoint (resource) for you API. Connexion also support a few methods of automatic routing if you do not want to explicitly define each supporting function.

With OpenAPI, you can also define and enforce requirements on the structure of the request. For a POST to /program, a request of content-type application/json is required and the contents must follow the ProgramList object schema.

paths:
  /program:
...
    post:
      operationId: "program.create"
      summary: "Create a NEW program."
      requestBody:
          description: "Program to create"
          required: True
          content:
            application/json:
              schema:
                x-body-name: "program"
                $ref: "#/components/schemas/ProgramList"

The ProgramList schema is also defined in the specification as an array of Program objects according to that corresponding schema as shown below.

The Program schema defines it be an object with an integer property called line and a string property called input. Both line and input are required for the Program object. (When creating a program on the Commodore 64, each program line must begin with an integer line number followed by a string of BASIC input for that line.)

components:
  schemas:
    Program:
      type: "object"
      required:
        - line
        - input
      properties:
        line:
          type: "integer"
        input:
          type: "string"
      example:
        line: 10
        input: "PRINT \"HELLO WORLD\""
    ProgramList:
     type: array
     items:
       oneOf:
       - $ref: "#/components/schemas/Program"

So when a request is handled by the create method in the program.py module, we can know exactly what to expect when it comes to the request contents.

# program.py

def create(program):

    PROGRAM = []

    for l in program:
        line = l.get("line")
        input = l.get("input")

        if input:
            element = {
                "input": input,
                 "line": line,
                 }
            PROGRAM.append(element)
        else:
            abort(
                406,
    			"Invalid program request",
        )

    with open(filename, "w") as file:
        file.write(json.dumps(PROGRAM)) # write PROGRAM to file

    return PROGRAM, 201

Swagger UI Documentation

Navigating to the /app/ui URL will show the very nice Swagger UI interactive REST documentation for C64aaS shown below. All of this is automatically generated by Connexion!

Swagger UI REST Documentation for C64 as a Service

C64aaS In Action

If you spin open the POST endpoint for Program in Swagger UI, you get an interactive form allowing you to specify the content-type and body of the Request. You can even put an example Request in your swagger.yml file, which will appear here to give the user a head start.

New Program API - Commodore 64 as a Service

Clicking the Execute button will send the request to your API application and receive the response, displaying the headers and body of each, as can be seen below.

You can also interact with the GET methods, supplying any accepted parameters to see the results returned by the API. Sending a GET to the Program endpoint returns the contents of the program that was just created when you submitted the POST above.

Sending a GET to the Run endpoint will instruct the API to run the Program you created with the Commodore BASIC interpreter running on the back-end server. The results of that program run are then sent back as a JSON response.

As you can see, the BASIC program we created with a POST to Program:

10 REM SAY HELLO
20 PRINT "HELLO ADAM"

Results in the following response from the Run endpoint of our API:

[ "HELLO ADAM" ]

For anyone who has programmed BASIC on 8-bit computers, you will know that infinite loops are common in example programs such as the famous 10 PRINT classic:

10 PRINT CHR$(205.5+RND(1)); : GOTO 10

The current version of Commodore 64 as a Service roughly supports support PETSCII graphics via a Unicode translation (see also this thread), so I wanted to make sure the API could handle a infinite loops. It does this by implementing a timeout on the execution of Run, returning the results produced before the program was stopped due to timeout.

Here is another even simpler program with an infinite loop:

10 PRINT "HELLO WORLD"
20 GOTO 10

Submitting this infinite loop to the GET Run API for Commodore 64 as a Service returns several thousand HELLO WORLD! lines as shown in the video below.

Commodore 64 as a Service was a fun way to learn Flask for Python using Connexion to support an “API first” implementation. It’s certainly a bit silly, but perhaps in a world filled with blockchain currencies, the metaverse, and ChatGPT large language models, then having Commodore 64 as a Service available for all will make the internet is a better place!

Sources and Credits:

The title image at the top of the post is “Cyberpunk Lamers” by Odyn1ec (Jarek Wyszyñski).

The tutorial mentioned is Python REST APIs With Flask and Connexion written by Philipp Acsany.

My Commodore 64 as a Service repository is available on GitHub.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Commodore 64 As A Service
  • Yum Yum Donut
  • Back to BASIC : Part 1
  • Iceland with Kids : Part 1
  • Action Potential

Recent Comments

  • Dan on Simple BBS in a Commodore Emulator
  • Steve Turgeon on Simple BBS in a Commodore Emulator
  • rtlprmft on Simple BBS in a Commodore Emulator
  • John Lansing on Simple BBS in a Commodore Emulator
  • Doug MacDonald on Simple BBS in a Commodore Emulator

Blogroll

  • ImaPenguin
  • C64 OS Weblog
  • Stephen Wolfram Writings

Archives

  • October 2023
  • March 2023
  • January 2023
  • October 2022
  • September 2022
  • August 2022
  • May 2022
  • February 2022
  • January 2022
  • December 2021
  • June 2017
  • January 2007
  • October 2006
  • August 2006

Categories

  • books
  • coffee
  • howto
  • life
  • outdoors
  • radio
  • retro
  • technology

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org
©2025 Adam Whitney | Powered by WordPress and Superb Themes!