blog:ql_dev1

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
blog:ql_dev1 [2021/12/14 09:55] johnblog:ql_dev1 [2021/12/16 11:03] (current) john
Line 1: Line 1:
 ====== Sinclair QL Development Tools ====== ====== Sinclair QL Development Tools ======
  
 +There are a range of development tools and languages for the QL, including the built-in SuperBASIC. But I am more interested in using the same type of tools that I use for the NEC PC-98, MS-DOS, X68000 and more; a reasonably standards compliant C compiler, assembler and linker.
 +
 +There are a few C compiler options for the Sinclair QL - which are summarised nicely here: http://www.dilwyn.me.uk/c/index.html
 +
 +I touch on a few of them, including the one I am using (C68, in it's modern cross-compiler incarnation) below.
 ==== C68 Self-hosted on the QL ==== ==== C68 Self-hosted on the QL ====
  
 +   * http://www.dilwyn.me.uk/c/index.html
 +
 +Purported to be the main C compiler for Sinclair QL systems. This is the original version intended to run on the QL itself. 
 ==== C68 Cross-Compiler (aka XTC68)  ==== ==== C68 Cross-Compiler (aka XTC68)  ====
  
-https://github.com/stronnag/xtc68+   https://github.com/stronnag/xtc68 
 + 
 +A port of C68 to DOS, Windows and Linux systems was made in the late 90's to earl 2000's. Unfortunately it has bit-rotted quite badly and does not run on any current systems. The Git repository above is a reworked, cleaned up version that compiles perfectly on modern 64bit Linux systems. 
 + 
 +As suggested, this runs as a cross-compiler; targetting the Sinclair QL QDOS runtime with the convenience of using all of the modern development tools, source code revision of a GNU/Linux system. 
 + 
 +I've posted a pre-compiled version of the toolchain, with the original C68 runtime libraries (crt.o, libc.a et-al) and system headers, below.
  
    * {{ :blog:sinclair:c68_linux64-2021_12_14.tgz |}} - Pre-compiled, with headers and runtime libraries. Unpacks to ///opt/toolchains/qdos//    * {{ :blog:sinclair:c68_linux64-2021_12_14.tgz |}} - Pre-compiled, with headers and runtime libraries. Unpacks to ///opt/toolchains/qdos//
 +   * {{ :blog:sinclair:c68.pdf |}} - C68 User Manual. For the original Sinclair QL hosted version, but most of the compiler and linker flags and options remain the same.
 +   * {{ :blog:sinclair:c68_documentation_-_dave_walker.pdf |}} - A more in-depth manual about the use of C68, including a //basic// reference of available calls.
  
 +=== Sample C68 Makefile ===
 +
 +You can of course use standard GNU Makefiles alongside C68, here's a basic one that compiles a single binary from two .C source files, as you would normally do with anything of reasonable complexity.
 +
 +Note that I specifically **do not** have my C68 toolchain directories in my path, as I have far too many compilers installed to manage them if they were all fighting for priority on the path. I tend to set things up so that I either temporarily add them to the front of the path, as per the makefile below, or have a shell script temporarily set the path for a given session.
 +
 +It's often easier to keep paths and things like that encapsulated in the Makefile, so you can see that at the top of the example below:
 +
 +<code>
 +# Sample makefile for C68 projects
 +
 +#################################
 +# Temporarily add the cross
 +# compiler location to the path
 +#################################
 +SHELL := /bin/bash
 +PREFIX = /opt/toolchains/qdos
 +PATH := ${PREFIX}/bin:$(PATH)
 +
 +#################################
 +# Locations and name of compilers 
 +# and friends
 +#################################
 +CC = qcc
 +LD = qld
 +AS = as68
 +CPP = qcpp
 +
 +#################################
 +# Paths to additional headers and
 +# library files
 +#################################
 +#INCLUDES = -I./my_includes -I./other_includes
 +INCLUDES = 
 +#LIBS = -L./my_libs -lmylib
 +LIBS = 
 +
 +#################################
 +# Compiler flags
 +#################################
 +ASFLAGS = 
 +LDFLAGS = -v
 +CFLAGS = -O
 +
 +#################################
 +# What our application is named
 +#################################
 +TARGET = game
 +
 +#################################
 +# Targets to build/run
 +#################################
 +all: $(TARGET)
 +#dev:
 +#test:
 +
 +#################################
 +# Object files needed to build our 
 +# main application
 +#################################
 +OBJFILES = test.o other.o
 +
 +#################################
 +# Main application target build recipe
 +#################################
 +$(TARGET):  $(OBJFILES)
 + $(LD) $(LDFLAGS) $(OBJFILES) $(LIBS) -o $(TARGET)
 +
 +###############################
 +# Clean up
 +###############################
 +clean:
 + rm -f *.o
 + rm -f $(TARGET)
 +
 +################################
 +# Each C source is compiled to
 +# an object file, to be linked
 +# as a single binary, above.
 +################################
 +test.o: test.c
 + $(CC) $(CFLAGS) $(INCLUDES) -c test.c
 +
 +other.o: other.c
 + $(CC) $(CFLAGS) $(INCLUDES) -c other.c
 +</code>
 +
 +=== Running C68 ===
 +
 +If you use a sample makefile such as the one above, you should get an output such as this:
 +
 +<code>
 +$ make
 +qcc -O  -c test.c
 +qcc -O  -c other.c
 +qld -v test.o other.o  -o game
 +ld v1.22  QL 68000 SROFF Linker
 +COMMENT: C68 crt_o v4.24f
 +COMMENT: C68 libc_a v4.24f
 +
 +---------------------------
 +SECTION      START   LENGTH
 +---------------------------
 +TEXT                 3AD6
 +DATA          3ad6      2FA
 +UDATA         3dd0      4EE
 +---------------------------
 +Program length       42be
 +Relocation table =      1ef
 +--------------------
 +Memory Usage           0%
 +Buffer Usage           0%
 +--------------------
 +game: dataspace 870 (366)
 +
 +Link completed
 +</code>
 +
 +This is mostly easy to understand - two passes of the compiler; one for each source file, then the linker takes the two object files, adds-in //crt.o// and any functions from the //standard library//. The output is a little more verbose than the usual compiler though - an important element being the //dataspace// that the resulting binary will take up in the QL memory. 
 +
 +If you hexdump the binary, you will see some trailing data that mirrors that //dataspace// number:
 +
 +<code>
 +$ hexdump -C game
 +.
 +.
 +.
 +00003fa0  04 04 04 04 04 04 04 04  04 0e 14 0c 04 04 0c 16  |................|
 +00003fb0  14 26 08 04 04 04 04 48  04 06 60 01 24 38 00 00  |.&.....H..`.$8..|
 +00003fc0  58 54 63 63 00 00 03 66                           |XTcc...f|
 +00003fc8
 +</code>
 +
 +This is referred to as the //xtcc field// and is used by the **qlzip** utility when unpacking on the QL native filesystem to restore the needed metadata about the application.
 ==== GCC Cross-Compiler ==== ==== GCC Cross-Compiler ====
 +
 +  * https://github.com/xXorAa/qdos-devel
 +
 +This has the potential to be a great resource for people such as myself who are primarily unix developers. Sadly, this was a project started a long time ago, and as such, is based on a horribly dated version of GCC 2.95.3 - almost 20 years old at the time of writing (2021).
 +
 +As such, it is a real pain to get working on any reasonably modern version of Linux. The project above has attempted to make it easier by building all of the tools inside an earlier Debian-based docker container, but I simply could not get it to work reliably; the install procedure is a bit of a mess of installing components to bootstrap the build of other components, then overwriting various binaries with the stages of later output. It's all a bit of a mess (and no fault of the guy trying to make it easier!).
 +
 +It should, in theory, build outside of a docker container, and I got it //mostly// working, but when invoking the built version of gcc, the linker stage of a test C program would blow up with a signal 6 (ABORT). 
 +
 +It's a shame, as other platforms (Atari ST, for example) that are very similar to the QL, have much more modern versions of the GCC
 +
 +==== Working with QL files & devices ====
 +
 +The QL has it's own filesystem and files have attributes which are not directly equivalent to anything on a modern filesystem - storing the size of the amount of memory needed to load the binary, for example. So there are some tools needed to get anything you are working on, say in a Linux filesystem, into a QL-native format.
 +
 +   * **qlzip**
 +     * https://github.com/xXorAa/qlzip/
 +     * Any binaries produced by C68 can be zipped up, have their extended data stored, and then unpacked with the correct data in place using a native QL unzip tool.
 +
 +   * **qltools**
 +     * https://github.com/NormanDunbar/qltools
 +     * Works with native QL floppy disks (DD 720K, HD 1440K or ED 3.2MB) or disk images to transfer files to/from those devices from Linux.
 +
 +==== Other Stuff ====
 +
 +I wrote a quick Python script to print out the value of an XTcc field in a binary produced by C68, GCC or similar. This is useful when copying an executable into a QL floppy or hard drive filesystem and you need to set the //dataspace parameter//:
 +
 +<code>
 +#!/usr/bin/env python
 +
 +####################################
 +#
 +# Prints out the value of a Sinclar
 +# QL binary XTcc field, as needed to
 +# turn it into an executable file
 +# on the QL itself, or when transferred
 +# using qltool.
 +#
 +# John Snowdon, 2021
 +#
 +####################################
 +
 +import sys
 +import os
 +
 +if len(sys.argv) < 2:
 + print("ERROR: No filename given")
 + sys.exit(-1)
 +
 +ql_filename = sys.argv[1]
 +if (os.path.exists(ql_filename)):
 + f = open(ql_filename, "rb")
 + filedata = f.read()
 + offset = filedata.find(b'\x00XTcc')
 +
 + if offset:
 + # First byte of "XTcc" is at 'offset', so jump by 4 bytes
 + f.seek(offset + 5)
 + dataspace = f.read(4)
 +
 + # Print out the value of the dataspace field
 + print(int.from_bytes(dataspace, byteorder='big'))
 +
 + f.close()
 +
 +else:
 + print("ERROR: Filename does not exist")
 + sys.exit(-1)
 +</code>
 +
 +Just call the script with the name of your C68 executable. For example:
 +
 +<code>
 +$ xtcc game.bin
 +868
 +</code>
 +
 +... the value printed is the size of the dataspace needed. So you would then copy and mark your binary with qltools as follows:
 +
 +<code>
 +$ xtcc game.bin
 +868
 +$ qltools floppy.img -W game.bin
 +$ qltools floppy.img -x game.bin 868
 +</code>
 +
 +Or even easier, embed the xtcc call in backticks:
 +
 +<code>
 +$ qltools floppy.img -W game.bin
 +$ qltools floppy.img -x game.bin `xtcc game.bin`
 +</code>
 +
  • blog/ql_dev1.1639475749.txt.gz
  • Last modified: 2021/12/14 09:55
  • by john