SMSQ/E boot sequence

By | 2016-03-17

I promised last time that I will only write a new post if I have something interesting to say. Today’s topic is interesting to maybe three people in the world, but I’m sure they will be thrilled. Today I was asked a question about the SMSQ/E boot sequence and it’s been so long that even I had to research again how it works, so I might as well write it up now. In the following text I’ll try to explain the early boot sequence of both QPC and Q40.

Attention: The whole text is based on me looking at the sources for roughly two hours. I took care to be accurate, but I’ve been wrong before in my life. Twice or so.

SMSQ/E is a bunch of modules

An SMSQ/E file or ROM consists of separate modules, one concatenated after the other (hardware initialisation, screen driver, disc driver, etc.). Every module has a header except the first one, the host module. As most SMSQ/E files can be executed directly from another host OS, the host module must start with code. It has a trailer instead, which is at the end of the whole SMSQ/E file (so one could say the host module includes all other modules).

ROM locations

On the Q40, the ROM is seen in two places: $00000000-$00018000 (only first 96kb) and $FE000000. The lower region has one special feature: when the ROM is visible, all write accesses go to a shadow RAM area (default mode). The hardware can switch the mode (by writing to address $FF018000) so that a read will be from the shadow RAM and any write will be ignored. The ROM is hidden in this case. The mode can be switched back by writing to address $FF010000.

The CPU will get its initial instruction pointer from address $00000004, i.e. in the lower ROM area. In case of SMSQ/E this will point to $FE000062, so execution continues in the upper ROM copy. The lower copy of the ROM is not used again after that.

On QPC the SMSQE.BIN file is simply loaded to $00030000 and the virtual CPU starts executing instructions from there.

1st module: Host module

Q40: sys_boot_q40_rom_asm

  1. Offset 0: File was loaded through LRESPR from another OS
    1. Disable interrupts etc.
    2. Copy the whole SMSQ/E binary to $28480 so that it is at a known and safe place
    3. Disable caches
    4. Jump to 2nd module
  2. On ROM entry point:
    1. Disable caches
    2. Setup dummy stack
    3. Clear first 256 bytes of legacy screen RAM at $00020000 for whatever reason. Probably for debugging purposes.
    4. Jump to 2nd module

QPC: smsq_qpc_host_asm

  1. Setup dummy stack
  2. Jump to 2nd module

2nd module: SMSQ loader

File: smsq_smsq_loader_asm

  1. Call hardware initialisation in 3rd module. This will return hardware dependent data that will be used in the rest of the code.
  2. Copy the code of sms_wbase to an area below RAMTOP. sms_wbase is a simple function that writes a word located in D0 to the address in A5. It is used to write to the low RAM area, which for Q40 needs the special bank switching mentioned above. In QPC the RAM area is nothing special and the whole routine consists of one instruction: move.w d0,(a5)+
  3. Update the vector sms.wbase with address of the newly copied sms_wbase
  4. Copy code area from label ldm_reset to lpm_resetend (the reset routines) to first slice in the module table (see below). The first slice is always at sms.base = $900.
  5. Copy remaining modules to available slices. If no fitting slice could be found, copy to just below RAMTOP. Copying is done by the routine returned by the hardware initialisation (q40_mdinst/qpc_mdinst).
    This means that after this step the original ROM or RAM copy of the SMSQ/E file is not used anymore!
  6. Jump to 4th module, which is the OS initialisation, but this time to the new RAM copy!

3rd module: Hardware initialisation

Q40: smsq_q40_hwinit_asm
QPC: smsq_qpc_hwinit_asm

  1. Execution starts at label hwinit
  2. Do the actual initialisation (scan RAM size, setup MMU, etc.)
  3. Setup data and pointers for SMSQ loader:
    1. d7 = RAMTOP
    2. a2 = config block
    3. a3 = sms_wbase (the routine mentioned above that copies d0 to (a5))
    4. a4 = module table. This is a table that denotes special regions (“slices”) of memory where modules can be copied to and that would otherwise be unused.
      On Q40 this is only one slice:
      “sms.base-4 to sms.base”. The first slice is special, it will always be used to host the reset routine, no matter the size. This is why it’s only 4 bytes
      On QPC there are 3 usable slices:
      “sms.base-4 to $4000”
      “$4200-4 to $c000”
      “$10000-4 to $17d00”
    5. a5 = loader communication block
    6. a6 = module initialisation function
      On Q40 this is q40_mdinst, on QPC this is qpc_mdinst. Both copy the memory pointed to by (a0) to 4(a1) up to (a2). The Q40 just adds the ROM bank switching to the mix.
  4. Return to SMSQ loader

4th module: OS initialisation

File: smsq_smsq_base_asm

  1. This starts at label smsq_base
  2. Initialise real stack now
  3. Jump to sms_reset
  4. Clear memory
  5. Initialise vector area ($0008-$0400)
  6. Initialise trap vectors
  7. Initialise system variables
  8. Initialise SBasic stuff
  9. Call init_ext, which will call the initialisation code of all remaining SMSQ/E modules
  10. … from here on Job 0 (SBasic) basically starts running and the OS initialisation is finished

The end

The whole boot scheme is pretty clever but also hard to understand at first glance. But it does the job of catering for many different platforms and usage scenarios (boot from ROM, loaded from other host OS etc.) very well. For me it was enjoyable to once again dig a bit deeper into the OS I once have loved and known so well and I hope at least 3 other will join me in my delight 😉

Updated with input from Peter Graf.

7 thoughts on “SMSQ/E boot sequence

  1. Neil R

    It is documentation like this that will come in handy to someone one day and for that you must be congratulated and thanked.

    Reply
  2. NormanDunbar

    Like it!

    So, that’s three comments, so we must be the ” three people in the world” who are thrilled! 😉

    Cheers,
    Norm.

    Reply
  3. Marcel Post author

    Thanks everybody! With the additional voices in the mailing list and private eMail we’re now up to 6 people! I guess this is the third time in my life that I was wrong 🙂

    Reply
  4. Per Witte

    Very nice, Marcel 🙂 Im glad you still have a soft spot for SMSQ/E – not least because there are plenty of dark corners still, should the muse ever move you..

    Reply
  5. David Westbury

    Good stuff! Helpful reference when trying to make sense of sources (especially if its been a while since last looked at).

    I did something similar in a bit more detail for Gold SMSQ/E v2.99 a few years ago (although it did take me considerably longer than 2 hours!).

    I recall that SMSQ/E prevents repeatedly booting itself through special LRESPR. If it finds requested file header has data length of 4 it checks extra info, if string matches current SMSQ/E version, it ignores call.

    Reply
    1. Marcel Post author

      The gold card boot process is interesting, too, especially with all the patching going on. I dug through it while trying to solve the problem of QUBIDE sometimes not working with SMSQ/E some 13 years or so ago, but can hardly remember a thing now, just that it was some uninitialized memory or something like that.

      That LRESPR is special casing this is interesting, I didn’t know that! Makes sense to prevent boot-loops but also a little bit fragile as it relies on the QL file header being intact. Well, most people will have an IF in their boot anyway.

      It’s interesting how this is done. The generator exe looks if the first module given to it is only 4 bytes long and in this case the information from it will be mirrored in the Extra-header of the SMSQ/E file. For the gold card the first module is smsq_smsq_vers, whose contents is the version. Q40 on the other hand doesn’t use this mechanism, even though it could.

      Reply

Leave a Reply to Neil R Cancel reply

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