437 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			437 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
 | 
						|
 | 
						|
# GLCC  2.0
 | 
						|
 | 
						|
 | 
						|
This LCC--derived compiler and C library targets the [Gigatron](http://gigatron.io) VCPU.
 | 
						|
It keeps many of the ideas of the previous attempt to port LCC to the
 | 
						|
Gigatron (pgavlin's).  For instance it outputs assembly code that can be parsed by
 | 
						|
Python and it features a linker writen in Python that can directly
 | 
						|
read these files. It also differs in important ways. For instance the
 | 
						|
code generator is fundamentally different.
 | 
						|
 | 
						|
## 1. Status
 | 
						|
 | 
						|
This project provides a complete toolchain and C library for ANSI C 1989.
 | 
						|
 | 
						|
### 1.1. Implementation details
 | 
						|
 | 
						|
Some useful things to know:
 | 
						|
 | 
						|
  * Types `short` and `int` are 16 bits long.  Type `long` is 32 bits
 | 
						|
	long. Types `float` and `double` are 40 bits long, using the
 | 
						|
	Microsoft Basic floating point format. Both long arithmetic or
 | 
						|
	floating point arithmetic incur a substantial speed penalty, 
 | 
						|
	vastly improved with the dev7 rom.
 | 
						|
	
 | 
						|
  * Type `char` is unsigned by default. This is more efficient because
 | 
						|
	the C language always promotes `char` values into `int` values to
 | 
						|
	perform arithmetic. Promoting a signed byte involves a clumsy sign
 | 
						|
	extension. Promoting an unsigned byte comes for free with most
 | 
						|
	VCPU opcodes. If you really want signed chars, use `signed char`
 | 
						|
	or maybe use the compiler option `-Wf-unsigned_char=0`. This
 | 
						|
	is not recommended. 
 | 
						|
 | 
						|
* The nonstandard type qualifier `__near` can be used to indicate
 | 
						|
  that the data lives in page zero. This allows the compiler to
 | 
						|
  generate more compact code and tells the linker to place such
 | 
						|
  variables in page zero. The nonstandard type qualifier `__far`
 | 
						|
  is also recognized but currently inoperative. It will eventually
 | 
						|
  indicate that the data lives in banked memory.
 | 
						|
 | 
						|
* The traditional C standard library offers feature rich functions
 | 
						|
  like `printf()` and `scanf()`. These functions are present but their
 | 
						|
  implementation requires a lot of space. Instead one can call some of
 | 
						|
  the lower level functions that either are standard like `fputs()` or
 | 
						|
  non standard functions such as `itoa()`, `dtoa()` whose prototypes
 | 
						|
  are provided by [`<gigatron/libc.h>`](include/gigatron/gigatron/libc.h).
 | 
						|
  
 | 
						|
* Alternatively one can completely bypass stdio and use the 
 | 
						|
  low-level console functions whose prototypes are provided in
 | 
						|
  [`<gigatron/console.h>`](include/gigatron/gigatron/console.h).
 | 
						|
  The function `cprintf()` has all the formatting abilities of `printf`
 | 
						|
  but saves memory by bypassing standard io and printing to the console.
 | 
						|
  The function `mincprintf()` further saves space but only recognizes
 | 
						|
  `%d` and `%s` in format strings.
 | 
						|
 | 
						|
* The include file [`<gigatron/sys.h>`](include/gigatron/gigatron/sys.h)
 | 
						|
  provides declarations to access the gigatron hardware and wrappers
 | 
						|
  to call native SYS routines.
 | 
						|
  
 | 
						|
* Many parts of the main library can be overriden to provide special 
 | 
						|
  functionalities. For instance the console has the usual 15x26 characters, 
 | 
						|
  but this can be changed by linking with a library that redefines 
 | 
						|
  what is in `cons_geom.c`. This is what happens when one uses argument
 | 
						|
  `-map=conx` to save memory with a 10x26 console. Another more 
 | 
						|
  important example is the standard i/o library. By default
 | 
						|
  it is only connected to the console and `fopen()` always fail.
 | 
						|
  But one just has to redefine a few functions to change that.
 | 
						|
  This is one happens when one compiles with `-map=sim` which
 | 
						|
  [forwards](gigatron/mapsim/libsim) all the stdio calls to 
 | 
						|
  the emulator. Note that such binaries only run in the command
 | 
						|
  line gigatron emulator `gtsim` because they attempt to 
 | 
						|
  communicate with `gtsim` to forward the stdio calls.
 | 
						|
  
 | 
						|
* Over time the linker `glink` has accumulated 
 | 
						|
  a lot of capabilites. It supports common symbols,
 | 
						|
  weak symbols, and conditional imports. It can synthetize magic lists
 | 
						|
  such as a list of initialization functions that are called before main,
 | 
						|
  a list of data segments to be cleared before running main, a list
 | 
						|
  of memory segments that can be used as heap by `malloc()`, or a list
 | 
						|
  of finalization functions called when the program exits.
 | 
						|
  Using `glink -h` provides some help. Using `glink --info` provides
 | 
						|
  help for specific memory maps. But the more advanced functions
 | 
						|
  are documented by comments in the [source](gigatron/glink.py)
 | 
						|
  or comments in the library source files that use them...
 | 
						|
  
 | 
						|
## 2. Compiling and installing
 | 
						|
 | 
						|
You can build GLCC from source using two methods.
 | 
						|
 | 
						|
* The first method relies on the traditional `make` command
 | 
						|
  using the usual Posix command line utilities. This is the method used
 | 
						|
  for development and therefore is the recommended method
 | 
						|
  for Linux machines or Macs. It can also be used
 | 
						|
  on Windows when compiling with `cygwin` (https://cygwin.org) 
 | 
						|
  or `mingw64/msys2` (https://www.mingw-w64.org/). 
 | 
						|
  
 | 
						|
* The second method relies on `cmake` (https://cmake.org)
 | 
						|
  and supports many different toolchains such as Microsoft
 | 
						|
  Visual Studio, etc.
 | 
						|
  
 | 
						|
### 2.1 Building gigatron-lcc with Make
 | 
						|
 | 
						|
Because the primary GLCC development platform is Linux. 
 | 
						|
building `gigatron-lcc` with make on a Unix platform 
 | 
						|
should be very easy provided that a C compiler, bison, 
 | 
						|
gnu-make >= 4.0, and python >= 3.8 are installed.
 | 
						|
Simply type:
 | 
						|
```
 | 
						|
$ git clone https://github.com/lb3361/gigatron-lcc.git
 | 
						|
$ cd gigatron-lcc
 | 
						|
$ make
 | 
						|
```
 | 
						|
Then you can either invoke the compiler from its build location `./build/glcc` or
 | 
						|
install it into your system with command
 | 
						|
```
 | 
						|
$ make PREFIX=/usr/local install
 | 
						|
```
 | 
						|
where variable `PREFIX` indicates where the compiler should be installed.
 | 
						|
This command copies the compiler files into `${PREFIX}/lib/gigatron-lcc/` 
 | 
						|
and symlinks the compiler driver `glcc`, the linker driver `glink`,
 | 
						|
and the simulator `gtsim` into `${PREFIX}/bin`. All the other files are located under 
 | 
						|
`${PREFIX}/lib/gigatron-lcc`. Note that this directory can be relocated
 | 
						|
elsewhere in the system as long as its contents is preserved.
 | 
						|
You just need to either invoke `glcc` using the full path of
 | 
						|
the `gigatron-lcc` directory. Alternatively you can place symbolic links
 | 
						|
to these files somewhere in the executable search path.
 | 
						|
 | 
						|
There is also 
 | 
						|
```
 | 
						|
$ make test
 | 
						|
```
 | 
						|
to run the current test suite. 
 | 
						|
 | 
						|
### 2.2 Building gigatron-lcc with CMake
 | 
						|
 | 
						|
The prerequisites are python >= 3.8 and cmake >= 3.16.
 | 
						|
 | 
						|
In order to compile with cmake, you must first create a build directory
 | 
						|
and invoke CMake from that build directory. 
 | 
						|
For instance, on a Unix machine, 
 | 
						|
```
 | 
						|
$ git clone https://github.com/lb3361/gigatron-lcc.git
 | 
						|
$ cd gigatron-lcc
 | 
						|
$ mkdir build
 | 
						|
$ cd build
 | 
						|
$ cmake .. 
 | 
						|
```
 | 
						|
 | 
						|
This operation creates a Makefile in the build directory.
 | 
						|
You can then compile with `make`
 | 
						|
```
 | 
						|
$ make
 | 
						|
```
 | 
						|
Then you can either invoke the compiler from its build location `./glcc` or
 | 
						|
install it into your system with command
 | 
						|
```
 | 
						|
$ cmake --install .  -DCMAKE_INSTALL_PREFIX=/usr/local
 | 
						|
```
 | 
						|
where variable `CMAKE_INSTALL_PREFIX` is the directory where glcc
 | 
						|
should be installed.  Note that this directory can be relocated
 | 
						|
elsewhere as long as the relative position of the glcc files
 | 
						|
is preserved.
 | 
						|
 | 
						|
 | 
						|
### 2.3 Windows notes
 | 
						|
 | 
						|
#### 2.3.1 Cygwin GLCC
 | 
						|
 | 
						|
Thanks to the feedback of axelb, you can use the `make` method
 | 
						|
to compile gigatron-lcc under cygwin. For this, you must first
 | 
						|
install cygwin >= 3.2 from http://cygwin.org, make sure to 
 | 
						|
select the packages `gcc-core`, `make`, `bison`, `git`, and `python3`, 
 | 
						|
then issue the `make`, `make install`, or `make test` 
 | 
						|
command from the cygwin shell.
 | 
						|
The main drawback of building `gigatron-lcc` under cygwin is
 | 
						|
that you have to execute it from the cygwin shell as well since
 | 
						|
it depends on the cygwin infrastructure.
 | 
						|
 | 
						|
#### 2.3.2 Native Windows GLCC 
 | 
						|
 | 
						|
For this, you need a native version of Python (https://python.org).
 | 
						|
It is also highly recommended to install Git-for-Windows (https://gitforwindows.org/)
 | 
						|
and a version of GNU make for windows, for instance using 
 | 
						|
Chocolatey (https://community.chocolatey.org/packages/make).
 | 
						|
 | 
						|
* A first option is to use the mingw64 compiler (https://mingw-w64.org).
 | 
						|
  This compiler comes in various guises. After a bit of experimentation,
 | 
						|
  the recommended approach is to download the 32 bits version of 
 | 
						|
  [Git for Windows SDK](https://github.com/git-for-windows/build-extra/releases/latest).
 | 
						|
  This is certainly an overkill, but a very reliable one.
 | 
						|
  You can then start the Git for Windows SDK shell, clone the Gigatron LCC repository,
 | 
						|
  and use the `make` method discussed above. A precompiled version with installation 
 | 
						|
  and usage instructions can be found in the forum 
 | 
						|
  post https://forum.gigatron.io/viewtopic.php?p=2484#p2484.
 | 
						|
  
 | 
						|
* A second option is to use the CMake approach with any supported toolchain.
 | 
						|
  For this you need to create a build directory and run the CMake program
 | 
						|
  to generate project files. Please follow the instructions that come
 | 
						|
  with CMake to select the proper toolchain, compile the project, and
 | 
						|
  optionally set CMAKE_INSTALL_PREFIX and trigger the installation target.
 | 
						|
 | 
						|
Both options create a `bin` directory with Window batch files, e.g
 | 
						|
`glcc.cmd`, etc., that can be used to invoke GLCC from the DOS command
 | 
						|
line, from PowerShell, or from the Git Bash. These batch files rely on
 | 
						|
the `py` launcher that is installed by default with recent versions of
 | 
						|
Python for windows. Just add this directory to the executable search
 | 
						|
path, e.g.  `PATH=/c/glcc/bin;%PATH%` at the DOS command line or
 | 
						|
`PATH=/c/glcc/bin:$PATH` at the Git Bash command line. T
 | 
						|
 | 
						|
## 3 Compiler invocation
 | 
						|
 | 
						|
Besides the options listed in the [lcc manual page](doc/lcc.1),
 | 
						|
the compiler driver `glcc` recognizes a few Gigatron-specific options.
 | 
						|
Additional options recognized by the assembler/linker `glink`
 | 
						|
are documented by typing `glink -h`
 | 
						|
 	
 | 
						|
  * Option `-rom=<romversion>` is passed to the linked and helps
 | 
						|
	selecting runtime code that uses the SYS functions implemented by
 | 
						|
	the indicated rom version. The default is `v5a` which does not
 | 
						|
	provide much support at this point.
 | 
						|
	
 | 
						|
  * Option `-cpu=[4567]` indicates which VCPU version should be
 | 
						|
    targeted.  Version 5 adds the instructions `CALLI`, `CMPHS` and
 | 
						|
    `CMPHU` that came with ROMv5a. Version 6, which comes with ROMvX0,
 | 
						|
    is not a strict supersed of version 6 because it changes the 
 | 
						|
    encodings of CMPHS/CMPHU. Version 7, which comes with DEV7ROM 
 | 
						|
    is a strict superset of version 5. Version 6 and 7 are mutually
 | 
						|
    incompatible. GLCC offers primary support for version 7 but can
 | 
						|
    generate version 6 encodings for some of its instructions.
 | 
						|
    The default CPU is the one implemented by the selected ROM.
 | 
						|
 | 
						|
  * Option `-map=<memorymap>{,<overlay>}` is also passed to the linker
 | 
						|
    and specifya memory layout for the generated code. The default
 | 
						|
    map, `32k` uses all little bits of memory available on a 32KB
 | 
						|
    Gigatron, starting with the video memory holes `[0x?a0-0x?ff]`,
 | 
						|
    the low memory `[0x200-0x6ff]`. There is also a `64k` map, a `128k` map, 
 | 
						|
    and a `conx` map which uses a reduced console to save memory.
 | 
						|
    Additional information about each map can be displayed by 
 | 
						|
    using option `-info` as in `glcc -map=sim -info`
 | 
						|
	
 | 
						|
    Maps can also manipulate the linker arguments, insert libraries,
 | 
						|
    and define the initialization function that checks the rom type
 | 
						|
    and the ram configuration. For instance, map `sim` produces gt1
 | 
						|
    files that only run in the emulator [`gtsim`](gigatron/mapsim),
 | 
						|
    redirecting `printf` and all standard i/o functions to
 | 
						|
    the emulator itself. This is my main debugging tool.
 | 
						|
	
 | 
						|
 | 
						|
## 3. Examples
 | 
						|
 | 
						|
### 3.1. Running the LCC 8 queens program:
 | 
						|
 | 
						|
```
 | 
						|
$ ./build/glcc -map=sim tst/8q.c 
 | 
						|
tst/8q.c:30: warning: missing return value
 | 
						|
tst/8q.c:37: warning: implicit declaration of function `printf'
 | 
						|
tst/8q.c:39: warning: missing return value
 | 
						|
$ ./build/gtsim -rom gigatron/roms/dev.rom a.gt1 
 | 
						|
1 5 8 6 3 7 2 4 
 | 
						|
1 6 8 3 7 4 2 5 
 | 
						|
1 7 4 6 8 2 5 3 
 | 
						|
1 7 5 8 2 4 6 3 
 | 
						|
2 4 6 8 3 1 7 5 
 | 
						|
2 5 7 1 3 8 6 4 
 | 
						|
2 5 7 4 1 8 6 3 
 | 
						|
2 6 1 7 4 8 3 5 
 | 
						|
2 6 8 3 1 4 7 5 
 | 
						|
2 7 3 6 8 5 1 4 
 | 
						|
2 7 5 8 1 4 6 3 
 | 
						|
2 8 6 1 3 5 7 4 
 | 
						|
...
 | 
						|
```
 | 
						|
 | 
						|
### 3.2. Running Marcel's simple chess program in gtsim:
 | 
						|
 | 
						|
I found this program when studying the previous incarnation 
 | 
						|
of LCC for the Gigatron, with old forums posts where Marcel
 | 
						|
mentionned it as a "stretch goal" for the compiler. The main
 | 
						|
issue is that MSCP takes about 25KB of code and 25KB of data
 | 
						|
meaning that we need to use the video memory. My main change 
 | 
						|
was to reduce the size of the opening book,
 | 
						|
but this is not enough. One could think about using
 | 
						|
the 128KB memory extention but this will require a lot
 | 
						|
of changes to the code. In the mean time. we can
 | 
						|
run it with the `gtsim` emulator which has no screen
 | 
						|
but can forward stdio.
 | 
						|
 | 
						|
```
 | 
						|
$ cp stuff/mscp/mscp0.c .
 | 
						|
$ cp stuff/mscp/book.txt .
 | 
						|
 | 
						|
# Using map sim with overlay allout commits all the memory
 | 
						|
$ ./build/glcc -map=sim,allout mscp0.c -o mscp0.gt1
 | 
						|
```
 | 
						|
 | 
						|
Now we can run it. Option -f in `gtsim` allows mscp to 
 | 
						|
open and read the opening book file `book.txt`.
 | 
						|
Be patient...
 | 
						|
 | 
						|
```
 | 
						|
$ ./build/gtsim -f -rom gigatron/roms/dev.rom mscp0.gt1
 | 
						|
 | 
						|
This is MSCP 1.4 (Marcel's Simple Chess Program)
 | 
						|
 | 
						|
Copyright (C)1998-2003 Marcel van Kervinck
 | 
						|
This program is distributed under the GNU General Public License.
 | 
						|
(See file COPYING or http://combinational.com/mscp/ for details.)
 | 
						|
 | 
						|
Type 'help' for a list of commands
 | 
						|
 | 
						|
8  r n b q k b n r
 | 
						|
7  p p p p p p p p
 | 
						|
6  - - - - - - - -
 | 
						|
5  - - - - - - - -
 | 
						|
4  - - - - - - - -
 | 
						|
3  - - - - - - - -
 | 
						|
2  P P P P P P P P
 | 
						|
1  R N B Q K B N R
 | 
						|
   a b c d e f g h
 | 
						|
1. White to move. KQkq 
 | 
						|
mscp> 
 | 
						|
```
 | 
						|
 | 
						|
Now you can type `both` to make it play against itself...
 | 
						|
 | 
						|
```
 | 
						|
mscp> both 
 | 
						|
book: (88)e4
 | 
						|
1. ... e2e4
 | 
						|
8  r n b q k b n r
 | 
						|
7  p p p p p p p p
 | 
						|
6  - - - - - - - -
 | 
						|
5  - - - - - - - -
 | 
						|
4  - - - - P - - -
 | 
						|
3  - - - - - - - -
 | 
						|
2  P P P P - P P P
 | 
						|
1  R N B Q K B N R
 | 
						|
   a b c d e f g h
 | 
						|
1. Black to move. KQkq 
 | 
						|
book: (88)c5
 | 
						|
1. ... c7c5
 | 
						|
8  r n b q k b n r
 | 
						|
7  p p - p p p p p
 | 
						|
```
 | 
						|
This slows down a lot when we leave the opening book.
 | 
						|
But it plays!
 | 
						|
 | 
						|
## 4. Internals
 | 
						|
 | 
						|
The code generator uses several blocks of page zero variables.
 | 
						|
The linker knows the page zero usage of each rom and keeps 
 | 
						|
track of all free and used page zero locations.
 | 
						|
 | 
						|
  *  The most important block of page zero variables contains 24
 | 
						|
     general purpose word registers named `R0` to `R23`. This block is
 | 
						|
     can be manually displaced using the command line option
 | 
						|
     `--register-base=0x90` for instance. Register pairs named `L0` to
 | 
						|
     `L22` can hold longs.  Register triplets named `F0` to `F21` can
 | 
						|
     hold floats. Registers `R0` to `R7` are callee-saved and are
 | 
						|
     often used for local variables. Registers `R8` to `R15` are used
 | 
						|
     to pass arguments to functions. Registers `R15` to `R22` are used
 | 
						|
     for temporaries.
 | 
						|
 | 
						|
  *  Additional registers include: word registers named `T0` to `T3`,
 | 
						|
     scratch bytes `B0` and `B1`, long accumulator `LAC`, long 
 | 
						|
     accumulator extension byte `LAX`, floating point sign and
 | 
						|
     exponent bytes `FAS` and `FAE`, and a stack pointer `SP`. ROMs
 | 
						|
     that provide suitable native support may dictate the location
 | 
						|
     some of these registers. Otherwise they are allocated by the
 | 
						|
     linker in the upper half ot page zero. In general, these
 | 
						|
     locations, as well as `sysArgs[0..7]`, can be used by the 
 | 
						|
     compiler or the runtime, and therefore are not safe to use
 | 
						|
     from C programs. 
 | 
						|
 | 
						|
   * Since the DEV7 rom offers a true 16 bits stack pointer, GLCC-2.0
 | 
						|
     makes `SP` equal to `vSP`, allowing the use of efficient opcodes
 | 
						|
     to access non-register local variables.
 | 
						|
 | 
						|
The function prologue first saves `vLR` and constructs a stack frame
 | 
						|
by adjusting `SP`. It then saves the callee-saved registers onto the
 | 
						|
stack.  Nonleaf functions save 'vLR' in the stack frame and copy the
 | 
						|
argument passed in a registers to their final location. In contrast,
 | 
						|
leaf functions keep arguments passed in registers where they are
 | 
						|
because these registers are no longer needed for further calls.  In
 | 
						|
the same vein, nonleaf functions allocate callee-saved registers for
 | 
						|
local variables, whereas leaf functions use callee-saved registers in
 | 
						|
last resort, often avoiding the construction of a stack-frame.
 | 
						|
Leaf functions that do not need to allocate space on the
 | 
						|
stack can use a register to save VLR and become entirely frameless.
 | 
						|
Sometimes one can help this by using `register` when declaring local
 | 
						|
variables. Saving `vLR` allows us to use `CALLI` as a long jump
 | 
						|
without fearing to erase the function return address.
 | 
						|
This is especially useful when one needs to hop over page boundaries.
 | 
						|
 | 
						|
The VCPU accumulator `vAC` is not treated by the compiler as a normal 
 | 
						|
register because there is essentially nothing the VCPU can do once the 
 | 
						|
accumulator is allocated to represent a particular variable or a temporary.
 | 
						|
This would force the compiler to spill its content to a stack location
 | 
						|
in ways that not only produce less efficient code, but often result
 | 
						|
in an infinite loop because the spilling code must itself use `vAC`.
 | 
						|
Instead, the GLCC code generator produces VCPU instructions in bursts 
 | 
						|
that are packed on a single line of the generated assembly code. 
 | 
						|
Each burst is in fact what LCC calls one instruction. Bursts are
 | 
						|
produced by subverting the mechanisms defined by LCC to construct 
 | 
						|
various parts of a typical CPU instruction such as the mnemonic, 
 | 
						|
the address mode, etc. The VCPU accumulator `vAC` is treated as a scratch 
 | 
						|
register inside a burst. Meanwhile LCC allocates zero page registers 
 | 
						|
to pass data across bursts. This approach avoid the spilling problems
 | 
						|
but sometimes needs improving because it does not keep track
 | 
						|
of what data is left on the accumulator after each burst.
 | 
						|
This has been improved by a `preralloc` pass that tries to eliminate
 | 
						|
temporaries that can be passed through vAC, and by a state machine
 | 
						|
in the instruction emitter which conservatively maintains assertions
 | 
						|
about register or accumulator equality which can be used to
 | 
						|
simplify the code.
 | 
						|
 | 
						|
The compiler produces a python file that first define a function for each
 | 
						|
code or data fragment. The file then constructs a module that
 | 
						|
holds a list of all the fragments, as well as all the imported and
 | 
						|
exported symbols. The linker/assembler `glink` can read such files
 | 
						|
or can read a library file that is merely the concatenation
 | 
						|
of individual modules.  Each fragment is represented as a function
 | 
						|
that calls predefined functions whose uppercase name mirrors the name
 | 
						|
of the instruction they emit. Additional functions implement synthetic
 | 
						|
opcodes that can be implemented differently by different VCPU versions.
 | 
						|
More predefined functions are used to define labels or control
 | 
						|
when to check for a page boundary. The source of truth for 
 | 
						|
all this is the file `glink.py`.
 | 
						|
 | 
						|
The linker collects all the code and data fragments generated by the compiler.
 | 
						|
It then analyzes import and exports to determine which ones should be
 | 
						|
kept. It tries hard to place short functions into single segments in order
 | 
						|
to avoid costly hops. Then it iterates until all symbols are resolved and
 | 
						|
all symbol value dependent code is stabilized. Finally it produces a
 | 
						|
familar `GT1` file.
 |