I've started hacking together support for some of the BBC Micro OS call routines. Naturally there are some differences in implementations (as this is a Z80 there isn't an X or Y register) but the addresses and functionality are similar.
As an example, in BBC BASIC you can output a byte to the VDU driver (and so output text, define viewports, draw shapes and so on) by using the VDU command, like this:
(VDU 16 = clear graphics screen - ie, what CLG normally does). This is good, but such functionality would be nice to have in assembly too so your snippets of inline Z80 code can also output text, graphics and so on. For this purpose, there's a routine at address &FFEE called OSWRCH
that outputs the byte in the accumulator, so the above could be replicated with the following:
BBC BASIC lets you intercept CALLs to the &FF80..&FFFF range (where the BBC Micro routines reside) and redirect them to somewhere more appropriate. However, this functionality does not
work in the assembler - and a CALL &FFEE will crash the calculator as &FFEE on the protected RAM page 0. Therefore, all OS calls are offset from &FF80..&FFFF to &4080..&40FF, and so
would be the equivalent inline assembly version.
There are a large number of useful routines (setting clocks, getting cursor positions, reading lines of text, handling files, finding out memory ranges). Some are available in BASIC, and some can be used to perform tricks in assembly that aren't possible in BASIC directly.
An example of this is the following assembly program. It can be used to work out the graphics origin. It works by moving the graphics cursor to (0,0) then reading back the current cursor position via the OSWORD OS command. OSWORD is a generic routine that takes a routine number in A and a pointer to a parameter block
in HL. In this case A is &09, which means "read the last two graphics cursor positions", and it outputs the cursor positions as an 8-byte block .
Code: Select all
10 REM OS call routine constants
50 DIM GetOrigin 90
60 FOR pass%=0 TO 1
90 OPT pass%*2
110 ; Check there are 2 arguments
120 LD A,(IX+0) : CP 2 : RET NZ
140 ; Check they are both integers
150 LD A,(IX+1) : CP 4 : RET NZ
160 LD A,(IX+4) : CP 4 : RET NZ
180 ; MOVE to the origin by invoking VDU 25,5,0;0;
190 LD A,25 : CALL OSWRCH
200 LD A,5 : CALL OSWRCH
210 ; Output 4 zeroes to move to (0,0).
220 LD B,4
230 .lp XOR A : CALL OSWRCH : DJNZ lp
250 ; Invoke OSWORD &0D to read last two cursor positions
260 LD HL,PrevX
270 LD A,&0D : CALL OSWORD
290 ; Now we know the origin we need to output it.
300 LD L,(IX+2) : LD H,(IX+3)
310 LD DE,(CurrX) : CALL OutputCoord
320 LD L,(IX+5) : LD H,(IX+6)
330 LD DE,(CurrY) : CALL OutputCoord
360 ; 8 bytes of storage for cursor positions
370 .PrevX : DEFW 0 : .PrevY : DEFW 0
380 .CurrX : DEFW 0 : .CurrY : DEFW 0
400 ; Outputs 16-bit value DE to 32-bit variable at HL
420 LD (HL),E : INC HL
430 LD (HL),D : INC HL
440 SLA D
450 SBC A,A
460 LD (HL),A : INC HL
470 LD (HL),A : RET
490 NEXT pass%
You could invoke it like this (note that VDU 29 sets the graphics origin):
Code: Select all
PRINT "Origin = (";x%;",";y%;")"
I appreciate this probably means nothing to people not familiar with BBC BASIC..!