News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_zhulien

Pros & Cons of a universal Z80 loader, the BSOS (Bootstrap or Bullshit?)

Started by zhulien, 16:03, 21 October 21

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

zhulien

Hi Everyone,


If you had a universal loader that allows your program to run on as many Z80 platforms as possible, would you use it?  The loader itself would be tweaked for each platform, but your program wouldn't need to be as such.  Think of it as a mini-CP/M type concept.


On the platform, the alternate registers are setup to be used as:


- a (nested interrupt disables)
- b (0)
- de (actual os entry point)


To make a BSOS Call somewhat like CP/M but now to allow it to work no matter where os_entry is on the platform.


ld a, 'A'
exx
ld c, FN_OUTCHAR
call os_entry


A minimal set of standard functions would be provided as proof of concept, such as:


- FN_GETPLATFORM
- FN_GETCAPABILITIES
- FN_CLEARSCREEN
- FN_OUTCHAR
- FN_INCHAR


And expanded upon from there.


Example code of loader to be tweaked with a shell app that outputs 'A' in a loop.




(bsos)


os_init: di
exx
and a ; initialise a' with the number of nested interrupt disables
ld bc, 0 ; initialise bc' with 0
ld de, os_entry ; initialise de' with os_entry
exx

call isr_init ; patch the original ISR for our own
ei

call spawn_shell ; launch the os shell here

call isr_restore ; restore the original ISR and memory config
ret ; back to BASIC


; os entry point
; entry: c' = function
; alternative register set must be selected
; exit: all registers corrupt except a', bc', de'
; standard register set is selected

os_entry: call os_di ; disable interrupts (alternate regs must be selected)
call mem_set ; set memory state here if required (ie enable ROM)


ld hl, bc ; lookup function stored in c'
add hl, hl ; ld b, 0 not required as already is 0 in b'
add hl, jumpblock

ld c, (hl) ; read the function entry into bc'
inc hl
ld b, (hl)


push bc ; prepare to call function entry in bc'
exx ; swap back to normal register set
ret ; call the function

call mem_restore ; restore memory state if required (ie disable ROM)
exx
call os_ei ; enable interrupts (alternate regs must be selected)
exx
ret ; return to the caller


; disable interrupts with nesting
; entry: a' = current nest count
; alternative register set must be selected
; exit: all registers preserved
; alternative register set is selected

os_di: di ; can disable always
inc a ; a'++
ret


; enable interrupts with nesting
; entry: a' = current nest count
; alternative register set must be selected
; exit: all registers preserved
; alternative register set is selected

os_ei: and a ; they weren't disabled if a' == 0
ret z ; so return

dec a ; they were disabled so a'--
ret nz ; if still a' is not 0 then return
ei ; otherwise enable interrupts again
ret


jumpblock: db _getplatform, _getcapabilities
db _clearscreen, _outchar
db _inchar

isr_init: ret
isr_restore: ret
isr_entry: ret


mem_set: ret
mem_restore: ret


spawn_shell: ret ; todo load the shell


_getplatform: ret
_getcapabilities: ret
_clearscreen: ret
_outchar: ret
_inchar: ret











(os_shell app)


FN_GETPLATFORM equ 0
FN_GETCAPABILITIES equ 1
FN_CLEARSCREEN equ 2
FN_OUTCHAR equ 3
FN_INCHAR equ 4


app_entry: jr app_main


app_header: defb 1 ; app header version
defb 1, 0, 0 ; app version
defs 'os_shell',0 ; app name


os_entry: push de ; call the os
ret
ret


; disable interrupts with nesting
; entry: a' = current nest count
; standard register set must be selected
; exit: all registers preserved
; standard register set is selected

safe_di: exx
di ; can disable always
inc a ; a'++
exx
ret


; enable interrupts with nesting
; entry: a' = current nest count
; standard register set must be selected
; exit: all registers preserved
; standard register set is selected

safe_ei: exx
and a ; they weren't disabled if a' == 0
jr z, safe_ei_ret ; so return

dec a ; they were disabled so a'--
jr nz, safe_ei_ret ; if still a' is not 0 then return
ei ; otherwise enable interrupts again
safe_ei_ret: exx
ret

app_main: ld a, 'A' ; display the leter A
exx
ld c, FN_OUTCHAR
call os_entry


call safe_di
; TODO do something with interrupts disabled
call safe_ei

jr app_main



zhulien

Forgot to mention why?


Why not allow the same program to work on:  AMSDOS, CP/M, CP/M+, Sega SC3000, ZX Spectrum, Enterprise 128, Tatung Einstein, etc...


I can see many people will say... games will suck, not necessarily, depends on how we provide access to graphics libraries.


But, aside from games.  Imagine your cool assembler or disassembler, or file utility just working on everything?  Like CP/M but hopefully easier to port BSOS and more modern universal hardware access.


I'd like my program to have a wide audience.

m_dr_m

Obvious pro: Z80 programmers unite!


Con: well some tools remains tied to the hardware. For orgams:
  * Bank connection (agree, we could build an abstraction around that).
  * More problematic: rupture + rasters for enhanced ergonomics.


Anyway that's a great idea I would happily use for the next iterations of CPC_T cruncher.

m_dr_m

Con: Yet another standard!


In which ways it would improve CP/M?

andycadley

I suspect that, much like CP/M, even little choices like where in the address space things need to live will break compatibility with some machines.


Even assuming you can use IM1, IY or the alternate registers breaks on a Sinclair machine for example.

zhulien

Quote from: m_dr_m on 17:21, 21 October 21
Con: Yet another standard!


In which ways it would improve CP/M?


It lets you run the software on the OS you choose, as long as there is a loader for it - the goal is to keep it small and sweet, drivers would be available for some stuff perhaps.

zhulien

Quote from: andycadley on 18:08, 21 October 21
I suspect that, much like CP/M, even little choices like where in the address space things need to live will break compatibility with some machines.


Even assuming you can use IM1, IY or the alternate registers breaks on a Sinclair machine for example.


I can understand IM1, but not sure why IY or alternate registers have issues - unless you are meaning you cannot use them when calling inbuilt ROM routines.

andycadley

They're used by the ROM, including the interrupt handler and so things break if they don't hold the expected values (unless you redirect interrupts in IM2, which is it's own problem)


Other systems will undoubtedly have their own quirks and Z80 really, really doesn't lend itself to entirely relocatable code. So it quickly becomes something of a headache. I think you'd be better off just having a bunch of machine specific source code libraries providing machine independent functionality. Then you could quickly build for different target platforms while keeping a relatively generic code base.

1024MAK

The workaround for address independent code (assuming the source code is available) is for an address translation program to run before jumping to the game/application code.

The non-use (or restoring the correct value) of certain Z80 registers only becomes a problem if you want to use the built in ROM routines or return to BASIC. It's not needed if neither of these happens.

The bigger problem is the very different input and output I/O hardware....

Mark
Looking forward to summer in Somerset :-)

m_dr_m



First of all, Zhulien, I wish to reiterate my appreciation for your efforts about unification/standardisation.
The CPC scene is quite weak, and would gain to be boosted by the more general Z80 scene.
Now you could argue that programming for Symbos would be a great way to reach other scenes, but I have some strong concerns about that path, despite loving @Prodatron and his work.


Also, I have a use case: langage ports! (see https://www.pouet.net/topic.php?which=12155&page=1#c571455)


I'd like to have them on at least two "platforms":
* CPC Firmware (sometimes referred as "AMSDOS")
  * I'm used to a fast boot and don't plan to degrade my quality of life.
  * Plus, unidos opens new perspectives (e.g. scriptable ftp accesses).
* CP/M
  * So it could benefit to a broader community.
  * So we could benefit from a broader community.
  * Leverage nice CP/M shell with pipeline.


That would demand to neatly isolate the calls to libraries, and surely make some bridges.
Per chance, Orgams supports this at least:



  if cpc:import "cpclib":end
  if cpm:import "cpmlib":end



In turns, this sweet modularisation would ease ports to Symbos or FutureOs or non-CPC platforms.


Now, as other pointed out, the BSOS would be insuffisant. That's also my point about assembly-time dispatch: each platform would have its own I/O lib, its own ORG address, which solves the relocation issue.
But then, the BSOS seems to become unnecessary.


Am I missing something?

ralferoo

This after all is exactly the problem CP/M tries to solve: abstracting basic I/O, abstracting disk I/O and then a command processor on top which gets replaced when you actually run something, leaving just the I/O functionality so the program can have as much RAM as possible.


For languages in particular, CP/M is a very good target as it's well standardised already and they could make it work on anything that supports CPM with little effort as long as they don't want to do any machine specific features.


In the early 90s, after I first used a speccy tape loader / saver from type-in in Amstrad Action, I was trying to make a cross-platform game for both platforms with a friend who had a Spectrum. But we spent all our times building the (terrible) framework to abstract the machines, we never actually got around to the game! But part of that is what keeps me mostly interested in tape 30 years later!

Powered by SMFPacks Menu Editor Mod