porting NITROS9 L2 - nealcrook/multicomp6809 GitHub Wiki

Multicomp6809 State at boot

After hardware reset, Multicomp6809 (hereafter "mco9") executes the Camel FORTH ROM. From the FORTH prompt, the word NITROS9 performs the primary bootstrap: it will load and execute a program from track 34 of the NITROS virtual disk image stored on the SD card. Specifically, the primary bootstrap looks like this:

: NITROS9 \ load track34 and start it
  \ disk is on SD at 0x2.8000 so track34 is at 0x2.84C8
  MMUMAP EFTOCD
  2 SDLBA2 84C8 2600   \ load from 0x2.84C8 to 2600
  12 SDRD256n 0 SDLBA2 \ 18 (hex 12) 256-byte sectors
  \ vectors to RAM expected by krn
  0100 DFF2 ! 0103 DFF4 ! 010F DFF6 ! \ SWI3 SWI2 FIRQ
  010C DFF8 ! 0106 DFFA ! 0109 DFFC ! \ IRQ  SWI  NMI
  CDTOCD 2602 PIVOT ;  \ restore map, start at 2602

Description:

  1. enable the MMU with 1-1 mapping (ie, low 8 8K blocks of RAM map to the 8 blocks of physical address space. MMU is enabled, ROM still enabled)
  2. the Fixed RAM Top (FRT) mode of the MMU is not enabled. An additional write to the MMU is enabled (eg, as part of the start-up code in REL) in order to enable this mode.
  3. load data from the SDcard: 18, 256-byte sectors from track 34 of the disk image loaded into memory starting at address $2600 (SD card blocks are 512 bytes. I fit 1 256byte sector into each block -- wasting half the block)
  4. RAM block 7 is obscured by the ROM. We want to load vectors into it. Therefore, remap it to block's 6 physical address space. Write the vectors, then map it back (for example, "0100 DFF2 !" is a 16-bit store of data $0100 to address $DFF2. After mapping this back it will appear at address $FFF2)
  5. Finally, (2602 PIVOT) disable the ROM and jump to address $2602 to enter the NITROS boot code.

On entry to the NITROS boot code.. the boot code sees a full 64Kbyte RAM address space (with the exception of the I/O region $FFD0-$FFDF), interrupts are disabled, timer is disabled, ROM is disabled, vectors are set up at the top of memory as expected by L2 NITROS, MMU is enabled with TR=0. If the MMU was to be disabled at this point, everything would continue quite happily because the virtual address space is an exact match for the physical address space.

NitrOS9 Level 1 vs Level 2 vs Level 3

Coco1 and Coco2 (neither of which have an MMU) originally ran OS9 Level 1. For Coco3 (which has an MMU in the chip called GIME), OS9 Level 1 version 2 was developed, and eventually morphed into OS9 Level 2.

NitrOS9 Level 1 and Level 2 correspond to the OS9 versions of the same name. NitrOS9 adds a couple more function calls?

Level 3 was a NitrOS9 development. It requires a Coco3 MMU. It contains additional features to manage system memory more efficiently.

In the Sourceforge repository, the Level 1 and Level 2 code has build-time options that allow a 6809 version or a 6309 version to be generated. I read elsewhere that the Level 3 code is only intended to run on a 6309 but I have not inspected the code to know if that is true. I'm not sure how mature the Level 3 code is or how many people are using it.

Bill Nobel has a github repository of Level 3 code (unchanged in 2 years), which seems to contain 3 variants of the code, but some useful background documentation. It's not clear to me whether the sourceforge repository is in sync with this or not.

Based on all this, it seemed advisable to concentrate on a Level 2 port rather than contemplating a Level 3 port.

References:

http://www.nitros9.org/documents.html

https://github.com/bnobel/Nitros9-Level-3

The boot process

The boot process involves two files:

  1. kernelfile - lives on track34. Contains modules rel, krn, booter. booter is a cut-down device driver that ONLY knows how to look in LSN0 of the boot device to find the start address of a file and then load it. For mc09 the boot devices is a virtual disk image on the SD card.
  2. bootfile - lives in the "normal" filesystem of the boot device. Contains, at a minimum, the modules needed to get the system up and running to a shell. Other modules can be hand-loaded after boot so there is no need to add too much stuff to the bootfile.

Each NitrOS9 module is completely relocatable. From the shell, you can load and unload any module (that's not in use) except that you cannot unload any of the modules that make up

Based on the stuff above, looks like we need this..

The makefile targets (level2/mc09l2/makefile) are named KERNELFILE and BOOTFILE_MC09SD, corresponding (in level2/mc09l2/bootfiles/makefile) to KERNEL_MC09SD and BOOTFILE_MC09SD.

KERNEL_MC09SD = $(MD)/rel_80 $(MD)/boot_sdc $(MD)/krn

(the _80 refers to 80-column output. boot_sdc contains a cut-down subset of the disk driver code; just enough to be able to read a file from a specified start LSN).

BOOTFILE_MC09SD = $(MD)/krnp2 $(MD)/ioman $(MD)/init
$(MD)/rbf.mn
$(MD)/dds0_80d.dd
$(MC09SDC_80D)
$(MD)/scf.mn
$(TERM_MC09)
$(PIPE)
$(CLOCK50HZMC09)

where: TERM_MC09 = $(MD)/mc6850.dr $(MD)/term_mc6850.dt $(MD)/term_mc6850_t0.dt $(MD)/term_mc6850_t1.dt MC09SDC_80D = $(MD)/mc09sdc.dr $(MD)/s0_80d.dd $(MD)/s1_80d.dd $(MD)/s2_80d.dd $(MD)/s3_80d.dd the kernelfile or the bootfile.

When you load modules manually, they are loaded starting at a page boundary. When you load modules as part of the kernelfile or bootfile, they are abutted so that (overall) they occupy less memory than if they were loaded manually.

The kernelfile and bootfile are each created simply by concatenating the composite modules. For the bootfile (in L1 and L2) and for the kernelfile (in L1) this means that the load address of a module -- other that the first module -- is not pre-defined, can be any byte location, and is subject to change if there are code changes in earlier modules in the concatenation. All of this is OK, because a module must be written to be position-independent. However, it does make debugging tricky because the source code/assembly listing has a code origin of 0 which will never match up with the actual execution address of a module.

The L2 kernelfile is special, because it is designed so that its parts: rel, krn, booter are in the specific order (1) rel (2) booter (3) krnP1, and each module contains pad bytes so that (1) the start address of each module is Known and Fixed and (2) the end address of the krn is right at the top of memory

Since each of the modules is relocatable, the "logical" start address in the assembly listings is 0 for each module. Therefore, when debugging you need to add an offset to the assembly listing address to make it correspond with the actual address.

When debugging the bootfile and the L2 kernelfile, this is messy, because the offset of a file can change each time you make a code change in any one of the modules. When debugging the L1 kernelfile, the padding in each module keeps the offsets constant, so that working out the correspondence between memory addresses and the assembly listing is as straightforward as it gets:

  • rel loads at address $2600 (its entry point is $2602) and then relocates itself to address $ED00
  • booter loads at address $EE30
  • krn loads at address $F000

Level 2 kernel versions

The Level 2 kernel files are in level2/modules/kernel. There are three versions of the kernel:

krn.asm
ccbkrn.asm
mc09krn.asm

Each version contains a set of "use" statements to pull in other chunks of code from the level2/modules/kernel tree. There are two kernel-specific "use"d files: fnproc.asm/ccbfnproc.asm and an fsrqmem.asm/ccbfsrqmem.asm. All the others are common between the two kernels.

"ccb" stands for CocoBoot and is a project by Brett Gordon, who also works on Fuzix. CocoBoot is intended as a better booting mechanism for a Coco. Since it seems to not be actively developed, I chose to use krn.asm as the starting point for producing mc09krn.asm. However, I patched the krn and ccbkrn sources to minimise the differences between them (mainly, taking comment additions from Brett) -- those changes are now in the main NitrOS9 repository. I also note that Brett's code fixes what looks like a bug in the krn.asm code; the mc09krn.asm uses that bug-fix and Brett's automatic code-padding change.

(For Level 1, the changes required to support mc09 in the kernel are very small, and so there is no mc09-specific Level 1 kernel).

References:

https://sourceforge.net/projects/cocoboot/ (NOT to be confused with https://github.com/hackndev/cocoboot)

https://code.google.com/archive/p/legsforth/

https://sites.google.com/site/cocoboot2/home

The Bootlist

On a built NitrOS-9 system disk there is a NITROS9/BOOTLISTS directory. The make process just copies in stuff from the bootlists directory of the tree. ie: dw.bl standard.bl

The bootlist provides a mechanism for creating a new/customised bootfile on a running system. First edit the bootlist, then run the mb script. This creates a new bootfile (presumably with the correct name and indexed by LSN0). The new bootfile would then be used after a reboot.

Bottom line: the bootlist is not something that we need to worry about during the porting process.

More Detail

There is more gory detail here: