Index: doc/cc8-manual.md ================================================================== --- doc/cc8-manual.md +++ doc/cc8-manual.md @@ -30,11 +30,11 @@ later in 1972. Much of PDP-11 Unix remained written in assembly until its developers decided to rewrite the operating system in C, for Version 4 Unix, released in 1973. That decision allowed Unix to be relatively easily ported to a wholly different platform — the Interdata 8/32 — in 1978 by writing a new code generator for the C compiler, then -cross-compiling everything. That success in porting Unix led to C’s own +cross-compiling everything. That success in porting Unix lead to C’s own success first as a systems programming language, and then later as a general-purpose programming language. Although we are not likely to use CC8 to write a portable operating system for the PDP-8, it is powerful enough to fill C’s original niche @@ -81,11 +81,11 @@ ## Requirements The CC8 system generally assumes the availability of: -* [At least 16 kWords of core](#memory) at run time for programs +* [At least 12 kWords of core](#memory) at run time for programs compiled with CC8. The [native OS/8 CC8 compiler passes](#ncpass) require 20 kWords to compile programs. CC8 provides no built-in way to use more memory than this, so you will probably have to resort to [inline assembly](#asm) or FORTRAN @@ -328,39 +328,32 @@ 1. **Recursion:** See [`FIB.C`][fib] for an example of this. 1. **Simple arithmetic operators:** `+`, `-`, `*`, `/`, etc. -1. **Bitwise operators:** `&`, `|`, `~` and `!` +1. **Bitwise operators:** `&`, ¦, `~` and `!` 1. **Simple comparison operators:** False expressions evaluate as 0 and true as -1 in two’s complement form, meaning all 1's in binary form. See the list of limitations below for the operators excluded by our "simple" qualifier. -1. **2-character operators:** `++`, `--`, `==`, `!=`,`>=`, `<=`, `&&`, - and `||`. Note that `++` and `--` are postfix only, and - that `&&` and `||` are [implemented as `&` and `|`](#2cbo). - -1. **Ternary operator:** The `?:` operator works as of May 2020; it may - be nested. +1. **A few 2-character operators:** `++`, `--` (postfix only) and `==`. 1. **Limited library:** See [below](#libc) for a list of library functions provided, including their known limitations relative to Standard C. There are many limitations in this library relative to Standard C or even K&R C, which are documented below. 1. **Limited structuring constructs:** `if`, `while`, `for`, etc. are - supported. There is a nesting limit of 10 which is rarely exceeded in - most applications. In addition, `switch` statements are now supported - via a code re-write in the C pre-processor (cc.sv). See [`FORTH.C`][forth] - for an example. + supported, but they may not work as expected when deeply nested or + in long `if/else if/...` chains. -[fib]: /doc/trunk/src/os8/examples/fib.c -[forth]: /doc/trunk/src/os8/examples/forth.c +[fib]: /doc/trunk/src/cc8/examples/fib.c + ### Known Limitations of the OS/8 CC8 Compiler The OS/8 version of CC8 supports a subset of the C dialect understood by @@ -370,33 +363,31 @@ a 12 bit integer, and any variable/array can interpreted as `int`, `char` or pointer. All variables and arrays must be declared as `int`. As with K&R C, the return type may be left off of a function's definition; it is implicitly `int` in all cases. - It is not necessary to give argument types when declaring function - arguments, but you must declare a return type with the OS/8 CC8 - compiler: + Because the types are already known, it is not necessary to give + types when declaring function arguments: - int myfn(n) { /* do something with n */ } + myfn(n) { /* do something with n */ } This declares a function taking an `int` called `n` and returning an `int`. Contrast the CC8 cross-compiler, which requires function argument - types to be declared but not the return type, per K&R C rules: + types to be declared, if not the return type, per K&R C rules: - int myfn(n) + myfn(n) int n; { - /* do something with n, then _maybe_ return something */ + /* do something with n */ } - The type int is mandatory for all functions. - - The cross-compiler supports `void` as an extension to K&R C. This type - is converted to `int` in the pre-processor. Similarly, the type `char` is - converted. These type may be used for readability purposes. + The cross-compiler supports `void` as an extension to K&R C, but the + native compiler does not, and it is not yet smart enough to flag + code including it with an error. It will simply generate bad code + when you try to use `void`. 2. There must be an `int main()`, and it must be the last function in the single input C file. Since OS/8 has no way to pass command line arguments to a program @@ -431,12 +422,11 @@ pass](#ncpass), implemented in `p8.c`, so it doesn’t need `#include` to make these things work.) * No conditional compilation: `#if`, `#ifdef`, `#else`, etc. - * [Inline assmembly](#asm) via `#asm` / `#endasm`. See - [`FIB.C`][fib] for an example + * [Inline assmembly](#asm) via `#asm`. 5. Variables are implicitly `static`, even when local. 6. Arrays may only be single indexed. See `PS.C` for an example. @@ -448,23 +438,23 @@ must instead be: int i; i = 5; -8. `&&` and `||` work, but because they - are internally converted to `&` and `|`, their precedence has - changed, and they do not short-circuit as in a conforming C - compiler. +8. There is no `&&` nor ¦¦, nor are there plans to add + them in the future. Neither is there support for complex relational + operators like `>=` nor even `!=`. Abandon all hope for complex + assignment operators like `+=`. - You can work around such differences with clever coding. For - example, this code for a conforming C compiler: + Most of this can be worked around through clever coding. For + example, this: if (i != 0 || j == 5) - should be rewritten for CC8 to avoid the precedence changes as: + could be rewritten to avoid both missing operators as: - if (!(i == 0) || (j == 5)) + if (!(i == 0) | (j == 5)) because a true result in each subexpression yields -1 per the previous point, which when bitwise OR'd together means you get -1 if either subexpression is true, which means the whole expression evaluates to true if either subexpression is true. @@ -500,41 +490,11 @@ while (1) { /* do something useful */; if (cond) break; } We have no intention to fix this. -12. As of May 2020, `switch` is implemented via re-write to cascading - `if`/`then` statements. There are a number of limitations to this - approach that a CC8 user should be aware of. - - The primary one to keep in mind is that that if you use a - memory-mutating expression in the `switch` clause with a conforming - C compiler, it is evaluated just once at the start of the block, but - in CC8, it is evaluated once for each generated `if` sub-expression - that the code visits. That is, you should not say things like this - in code meant to be compiled with the CC8 native compiler: - - switch (*p++) {...} - - Say instead: - - int temp = *p++; - switch (temp) {....} - - Also, there **must** be a `default` case, and cases (including the - default case) must be terminated with a `break`. CC8 does not allow - for cases that fall through to the following case. The following - code has at least three syntax errors: - - switch (x) { - case 1: foo(); - case 2: bar(); - default: qux(); - } - -13. `sizeof()` is not implemented. - +12. `switch` doesn't work. #### GOVERNMENT HEALTH WARNING @@ -698,14 +658,13 @@ functions you’d think would go along nicely with those we do provide, such as `feof()` or `fseek()`. Keep in mind that the library is currently restricted to [a single 4 kWord field](#memory), and we don’t want to lift that restriction. Since the current implementation nearly fills that space, it is unlikely that we’ll add much more -functionality beyond the currently provided 33 LIBC functions plus [the -9 additional functions](#addfn). If we ever fix any of the limitations -we’ve identified below, consider it “gravy” rather than any kind of -obligation fulfilled. +functionality beyond the currently provided 33 functions. If we ever fix +any of the limitations we’ve identified below, consider it “gravy” +rather than any kind of obligation fulfilled. Some of these missing functions are less useful in the CC8 world than in more modern C environments. The low-memory nature of this world encourages writing loops over [word strings](#wordstr) in terms of pointer arithmetic and implicit zero testing (e.g. `while (*p++) { /* @@ -751,29 +710,22 @@ [iot]: /wiki?name=IOT+Device+Assignments [libch]: /doc/trunk/src/cc8/include/libc.h [libcsrc]: /doc/trunk/src/cc8/os8/libc.c -### `int atoi(s, *result)` +### `atoi(s, outlen)` -Takes a null-terminated ASCII character string pointer `s` and tries to -interpret it as a 12-bit PDP-8 two’s complement signed integer, storing -the value in `*result` and returning the number of bytes in `s` -consumed. +Takes a null-terminated ASCII character string pointer `s` and returns a +12-bit PDP-8 two’s complement signed integer. The length of the numeric +string is returned in `*outlen`. **Standard Violations:** -* Instead of returning the converted integer, this function stores - that value in `*result`. - -* Whereas `atoi()` in Standard C returns the converted value, in this - function, `s[retval]` is the first invalid — non-sign, non-digit, - non-space — character in the string, where `retval` is the return - value. - * Skips leading ASCII 32 (space) characters only, not those matched by [`isspace()`](#isspace), as the Standard requires. + +* The `outlen` parameter is nonstandard. ### `cupper(p)` Implements this loop more efficiently: @@ -946,11 +898,11 @@ requires you to open the input file before opening an output file when you need both. It may not be possible to fix this within the current limitations on the library, but if you come up with something, [we accept patches][hakp]. -[hakp]: /doc/trunk/CONTRIBUTING.md#patches +[hakp]: /doc/trunk/HACKERS.md#patches ### `fprintf(fmt, args...)` Writes its arguments (`args`...) to the currently-opened output file @@ -1284,16 +1236,20 @@ character will not be written! * There is no `snprintf()`, `vprintf()`, etc. -### `fscanf`, `scanf`, `sscanf` +### `sscanf` -Parse strings according to a `printf`-like format specification. `scanf` -gets the string from the interactive terminal, `fscanf` gets it from a -file opened with [`fopen()`](#fopen), and `sscanf` gets it from a -NUL-terminated C string already in core. +Reads formatted input from a file. + +**Standard Violations:** + +* `[f]scanf()` is not provided. Call [`[f]gets()`](#gets) to get a + string and then call `sscanf()` on it. + +* This list cannot possibly be complete. **DOCUMENTATION INCOMPLETE** ### `strcat(dst, src)` @@ -1393,39 +1349,10 @@ Prints the CC8 compiler’s banner message. This is in LIBC only because it’s called from several places within CC8 itself. **Nonstandard.** - -## Additional Utility Routines - -The functions that CC8 uses to manipulate the software stack are also -available to be called by end-user programs: `PUSH`, `POP`, `PUTSTK`, -`POPRET`, and `PCALL`. The page zero pointers for this stack are -initialized by code in `header.sb`, which is injected into your -program’s startup sequence during compilation. - -In addition, there are a set of functions that may be used to provide -temporary storage in field 4, acting like a temporary binary file: - -`void iinit(int address)`: Reset the file pointer to an arbitrary -address range 0-4095. - -`void stri(int value)`: Store ‘value’ at the current address, and -increment the address pointer. - -`int strl()`: Read the word at the current address, and do not increment -the address. - -`int strd()`: Read the word at the current address, and increment the -address. - -As field 4 is not used by OS/8, your program may use the entire field. -This library code does not check for overflow: going beyond address 4095 -will simply wrap to address 0. - - ## Trying the Examples The standard PiDP-8/I OS/8 RK05 boot disk contains several example @@ -1444,38 +1371,24 @@ The other examples preinstalled are: * **calc.c** - A simple 4-function calculator program. -* **pd.c** - Shows methods for doing double-precision - (i.e. 24-bit) integer calculations. - -* **hlb.c** - Generates [Hilbert curves][hc] on a Tek4010 - series display using raw terminal codes. Therefore, you must be - running a Tek4010 emulator when running this program, else you will - get garbage on the display! - * **fib.c** - Calculates the first 10 Fibonacci numbers. This implicitly demonstrates CC8's ability to handle recursive function calls. -* **basic.c** - A simple Basic interpreter used to test - a simple recursive expression processor. - -* **forth.c** - A simple Forth interpreter used to test - switch statemments etc. - -The two interpeters are quite complex, particularly the Forth -interpreter, which contains 300 lines of code and implements a number of -basic Forth functions. This example is intended to show what can be -crammed into 4k of core. - +If you look in `src/cc8/examples`, you will find these same programs +plus `basic.c`, a simple BASIC language interpreter. This one is +not preinstalled because its complexity is currently beyond the +capability of the OS/8 version of CC8. To build it, you will have +to use [the cross-compiler](#cross), then assemble the resulting +`basic.sb` file under OS/8. Another set of examples not preinstalled on the OS/8 disk are `examples/pep001-*.c`, which are described [elsewhere][pce]. -[hc]: https://en.wikipedia.org/wiki/Hilbert_curve [pce]: /wiki?name=PEP001.C ## Making Executables @@ -1512,11 +1425,10 @@ **Field 2:** The program's executable code **Field 3:** The LIBC library code -**Field 4:** (Optional) see the binary utilities above (stri...). ### OS/8 Reservations The uppermost page of fields 0 thru 2 hold the [resident portion of OS/8][os8res] and therefore must not be touched by @@ -1670,19 +1582,19 @@ [v7ux]: https://en.wikipedia.org/wiki/Version_7_Unix ### There Are No Storage Type Distinctions -Literals are placed in the same field as globals and the call stack, -rather that inline within the generated executable code. This may cause -surprise size limitations of the user programs. - -CC8 does it this way because the FORTRAN II / SABR system does allow any -initialisation of COMMON storage in field 1, so the literals have to be -stored in the user program page and then be copied into field 1 at -program initialization time. Various pointers to these regions are -mainatined by the compiler. +It may surprise you to learn that literals are placed in the same field +as globals and the call stack. + +Other C compilers place literals in among the executable code instead, a +fact that’s especially helpful on [Harvard architecture +microcontrollers][harch] with limited RAM. We don’t do it that way in +CC8 because literals are implemented in terms of the SABR `COMMN` +feature, which in turn is how OS/8 FORTRAN II implements `COMMON`. These +subsystems have no concept of “storage type” as in modern C compilers. ### Stack Overflow Since CC8 places the call stack immediately after the last literal @@ -1693,10 +1605,12 @@ try to push too much onto the stack, it will simply begin overwriting the page OS/8 is using at the top of field 1. If you manage to blow the stack by more than a page without crashing the program or the computer first, the [stack pointer will wrap around](#ptrwrap) and the stack will begin overwriting the first page of field 1. + +[harch]: https://en.wikipedia.org/wiki/Harvard_architecture ### Field Layout, Concrete Example The field layout given [at the start of this section](#memory) is not @@ -2009,11 +1923,11 @@ |`JMPI` |5400 |same as `JMP I` in PAL8| |`MQL` |7421 |load MQ from AC, clear AC| |`ACL` |7701 |load AC from MQ (use `CLA SWP` to give inverse of `MQL`)| |`MQA` |7501 |OR MQ with AC, result in MQ| |`SWP` |7521 |swap AC and MQ| -|`DILX` |6053 |set VC8E X coordinate (used by [`dispxy()`](#dispxy))| +|`DILX` |6053 |set VC8E X coordinate (used by [`dispxy()`](#dispxy)| |`DILY` |6054 |set VC8E Y coordinate| |`DIXY` |6054 |pulse VC8E at (X,Y) set by `DIXY`,`DILY`| |`CDF0` |6201 |change DF to field 0| |`CDF1` |6211 |change DF to field 1| |`CAF0` |6203 |change both IF and DF to field 0| Index: examples/pep001.bas ================================================================== --- examples/pep001.bas +++ examples/pep001.bas @@ -1,8 +1,5 @@ -1 REM Copyright (c) 2016 by Warren Young -2 REM Released under the terms of ../SIMH-LICENSE.md -3 REM ------------------------------------------------------------------ 10 FOR I = 1 TO 999 20 A = I / 3 \ B = I / 5 30 IF INT(A) = A GOTO 60 40 IF INT(B) = B GOTO 60 50 GOTO 70 ADDED media/etos/etosv5b-demo.rk05 Index: media/etos/etosv5b-demo.rk05 ================================================================== --- /dev/null +++ media/etos/etosv5b-demo.rk05 cannot compute difference between binary files Index: src/pidp8i/gpio-common.c.in ================================================================== --- src/pidp8i/gpio-common.c.in +++ src/pidp8i/gpio-common.c.in @@ -1,9 +1,9 @@ /* * gpio-common.c: functions common to both gpio.c and gpio-nls.c * - * Copyright © 2015 Oscar Vermeulen, © 2016-2019 by Warren Young + * Copyright © 2015 Oscar Vermeulen, © 2016-2018 by Warren Young * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, @@ -36,14 +36,10 @@ #include "pidp8i.h" #include -#if defined(HAVE_BCM_HOST_H) -# include -#endif - #include #include #include #include @@ -237,15 +233,26 @@ } } //// bcm_host_get_peripheral_address /////////////////////////////////// -// Provide fallback for non-Pi case to avoid a link error. +// Find Pi's GPIO base address -#if !defined(HAVE_BCM_HOST_H) - static unsigned bcm_host_get_peripheral_address(void) { return 0; } -#endif +static unsigned bcm_host_get_peripheral_address(void) +{ + unsigned address = ~0; + FILE *fp = fopen("/proc/device-tree/soc/ranges", "rb"); + if (fp) + { unsigned char buf[4]; + fseek(fp, 4, SEEK_SET); + if (fread(buf, 1, sizeof buf, fp) == sizeof buf) + address = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0; + fclose(fp); + } + + return address == ~0 ? 0x20000000 : address; +} //// DOUBLE BUFFERED DISPLAY MANIPULATION FUNCTIONS //////////////////// //// swap_displays //////////////////////////////////////////////////// Index: tools/bosi ================================================================== --- tools/bosi +++ tools/bosi @@ -1,312 +1,186 @@ -#!/bin/bash +#!/bin/bash -x # bosi - The Binary OS Image creation/update script -# -# Copyright © 2016-2021 by Warren Young -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, -# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR -# THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -# Except as contained in this notice, the names of the authors above shall -# not be used in advertising or otherwise to promote the sale, use or -# other dealings in this Software without prior written authorization from -# those authors. - - -# Display the usage message -function usage() { - cat < [tag] - - The available verbs are init, build, prepare, image, and finish. - - You may include a tag parameter with the 'image' and 'finish' verbs - to override the default tag ('ils') used in image and zip file - outputs. - - See RELEASE-PROCESS.md for more info. - -USAGE - exit 1 -} verb="$1" -tag="$2" -test -n "$verb" || usage -test -z "$tag" && tag=ils +object="$2" + +tag=$(echo $verb | cut -f2 -d- -s) +test -z "$tag" && tag=all nu=pidp8i nh=/home/$nu repo=pidp8i dldir="$HOME/tangentsoft.com/dl" -os=buster-lite +os=jessie-lite img=$dldir/pidp8i-$(date +%Y.%m.%d)-$tag-$os.img +rdisk=/dev/rdisk10 greadlink=$(type -p greadlink || type -p readlink) this=$($greadlink -f $0) -topdir="$($greadlink -f "$(dirname "$this")"/..)" - - -# Give user a few seconds to read the final messages before rebooting or -# powering off. -function do_wait() { - n=8 - cat < $mf < /dev/null + then + if [ "$USER" != "root" ] + then + echo "The init step has to be run as root." + echo "See RELEASE-PROCESS.md." + echo + exit 1 + fi + + usermod \ + -d $nh \ + -l $nu \ + -p $(openssl passwd -1 edsonDeCastro1968) \ + -m pi + fi + + sudo apt-get update && sudo apt-get -y upgrade || true + do_fossil +} + + +# Fossil clone, build and install +function do_fossil() { + test "$USER" = "root" && exec sudo -i -u $nu $nh/bosi fossil $object + cd $HOME + + test -n "$(type -p fossil)" || sudo apt-get -y install fossil + + if [ ! -d museum ] + then + mkdir -p museum $repo + fossil clone https://tangentsoft.com/$repo museum/$repo.fossil + fi + + cd $repo + + if [ -r ChangeLog.md ] + then + fossil revert # just in case + fossil update $object + else + fossil open ~/museum/$repo.fossil $object + ./configure + fi + + make -j4 + sudo make install || true + sudo reboot +} + + +# This script resets the OS's configuration to a clean first boot state. +function do_reset() { + history -c ; rm -f ~/.bash_history + test "$USER" = "root" || exec sudo $this reset + + shred -u /etc/ssh/*key* + dphys-swapfile uninstall + dd if=/dev/zero of=/junk bs=1M || true # it *will* error-out! + rm /junk + passwd -e pidp8i + ( sleep 1 ; poweroff ) & exit +} + + +# Shrink the filesystem on the SD card we're about to image to just a +# bit bigger than required to hold its present contents. +# +# The extra 100 megs in the arithmetic below accounts for the /boot +# partition, since the `resizepart` command takes a partition end value, +# not a size value. +# +# We don't calculate the actual end of the /boot partition and use that +# value because we want a bit of slack space to buy time for an end user +# who neglects to expand the card image into the free space available on +# their SD card after first boot. +function do_shrink() { + test "$USER" = "root" || exec sudo $this shrink $object + + umount /dev/sda2 || true # might auto-mount, might not + e2fsck -f /dev/sda2 # resize2fs demands it + blocks=$( + resize2fs -M /dev/sda2 2>&1 | + grep 'blocks long' | + grep -wo '[0-9]\+' + ) + if [ "$blocks" -gt 0 ] + then + parted /dev/sda resizepart 2 $(($blocks * 4096 + 10**8))b + blocks=$( + resize2fs /dev/sda2 2>&1 | + grep 'blocks long' | + grep -wo '[0-9]\+' + ) + + cat < [object] + + The available verbs are init, fossil, shrink, image, and finish. + + You may append a tag to the image and finish verbs (e.g. image-nls) + to override the default tag ('all') used in image and zip file + outputs. + + The object depends on the verb. See RELEASE-PROCESS.md. - trash $img - cd $dldir/.. - make synch +USAGE + exit 1 } # Main routine set -e case "$verb" in - in*) do_init ;; - bu*) do_build ;; - pr*) do_prepare ;; - im*) do_image ;; - fi*) do_finish ;; - *) usage ;; + in*) do_init ;; + fo*) do_fossil ;; + re*) do_reset ;; + sh*) do_shrink ;; + im*) do_image ;; + fi*) do_finish ;; + *) usage ;; esac