title 'LOADBGPR.ASM - Loader for BGPRINT 6/22/85' ;-----------------------------------------------------------------------; ; Copyright (c) 1985 by: ; ; ; ; Plu*Perfect Systems ; ; Box 1494 ; ; Idyllwild Ca 94249 ; ; ; ; Licensed users of The Backgrounder are granted permission to use ; ; this program for non-commercial purposes provided copyright ; ; notices are retained intact. ; ; ; ; Please send a copy of improved versions to Plu*Perfect Systems. ; ;-----------------------------------------------------------------------; ; ; The code bordered by ***** is module-specific. ; $-MACRO vers equ 1$1 ;version 1.1 2/17/86 changed test for presence of non perm modules ; to handle case where bios warm boot routine ; is in first page of BIOS. Also added flag ; for optional parallel pause method ; ;version 1.0 1/13/86 added parameter check with minor change to data structure ; ; Offsets from base of permanent module for module header ; Odisplay equ 0 ;jmp Owbootent equ 3 ;jmp Oremoveent equ Owbootent+3 ;jmp Owbootaddr equ Oremoveent+3 ;word Oprotect equ Owbootaddr+2 ;word Oname equ Oprotect+2 ;word Ouwboot equ Oname+2 ;jmp ; structure of the page-relocatable .PRL file: ; PARAMETERS ; CODE at PARAMETERS +100H ; RELOCPAGE EQU 7 PARAMPAGE EQU RELOCPAGE+1 CODEPAGE EQU PARAMPAGE+1 maclib z80 maclib bgprint NEEDBG equ FALSE ;Backgrounder not required by this module ;NOTE --- if you are running an '83 ;Kaypro without the TURBOROM and using a ;parallel printer, then you must use the ;Backgrounder. This is because there is ;a bug in Kaypro's monitor ROM that incorrectly ;always returns TRUE for liststatus, this will ;make the "pause" function fail. DEBUG equ FALSE ;allows it to run under debugger aseg org 100h ; jmp start ;***************************************************************** ; ; DATA AREA FOR MODULE - ; Placed at 103h for ease of patching without reassembly. ; Assemble your own convenient set of special characters and pause ; character here. ; ;size parameters for checking compatibility of seperate assembled modules lmaxchr:db MAXCHR ;for checking by config and loader lmaxstr:dw MAXSTRS ;for checking by config and loader ;parameters moved to high module by loader lmethod:db 00H ;parallel printer pause method ; 00 - use busy line transitions ; FF - use serial printer keypress method lpausch:db 07fh ;the pause character lprefch:db '\' ;prefix character lchrcnt:db ccnt ;count of special characters + 1 ;the special characters ;sample set of strings for OKIDATA 82 lchrtbl:db '6' ;set 6 lines /inch db '8' ;set 8 lines /inch db 'C' ;turn on compressed db 'c' ;turn off compressed ccnt equ $-lchrcnt ; ; each string is: length,char1,...,charn ;--------------------------------------------- ;macro to generate standard vriable length string varstr MACRO string LOCAL startstring LOCAL endstring db endstring - startstring startstring: db string endstring: ENDM ;--------------------------------------------- ; these are test strings for the OKIDATA 82 printer ; lstrngs: VARSTR VARSTR VARSTR 01DH VARSTR 01EH ; ;leave room for the maximum set of characters and strings ; ds (MAXCHR-ccnt) + MAXSTRS-($-lstrngs) ; ; ; DATA AREA FOR LOADER ; gotoccp:db 0 ;NZ if module should jmp to ccp entry ydata: dw 0 ;addresses of data areas ystrtbl:dw 0 ;in high module ystrngs:dw 0 ; param$offset: dw 0 ;base of relocated parameters offset: dw 0 ;base of module in high memory wboot$addr: dw 0 ;addr at 0001h biosbase: dw 0 ;base of bios jmp vector ccpbase: dw 0 ;base of ccp wbpointer: dw 0 ;addr at biosbase + 4 bg$base: dw 0 ;base of The Backgrounder ; obios: ds 33h ;copy of original bios vector ds 48 ;loader's stack stack: ds 0 savstk: dw 0 ; banner: db CR,LF modstr: DB 'BGPRINT' ;this name must agree ;with that at 'xname' in module modslen equ $-modstr db ' v ',(vers/10) + '0','.', (vers mod 10) +'0' db ' Character-translation printer driver',CR,LF ; ; NOTE: Add any reminder message here about which special ; characters are in effect. ; crlf: db CR,LF,'$' ; ;***************************************************************** ; START: sspd savstk lxi sp,stack call signon call ckenv ;check runtime environment call install call patchin call movdata call saymod rst 0 ;warm boot to secure module ; PRABORT: call print ;print msg in de and abort abort: lspd savstk ret ;---------------------------------------- PAGE ; ; check the system environment before loading ; CKENV: call testlow ;any modules below the bdos now? IF NEEDBG lxi d,nobgmsg jc prabort ;CY if nothing below the bdos call testnonperm IF NOT DEBUG lxi d,nonpermmsg ;for debug, zap this error jnz prabort ENDIF ; call findbg ;find bg module lxi d,nobgmsg jc prabort ; ELSE rc ;nothing to test rz ;handle case where warmboot code ;is in first page of bios (DJM 2/18/86) call testnonperm IF NOT DEBUG lxi d,nonpermmsg ;for debug, zap this error jnz prabort ENDIF ; ENDIF lxi h,modstr ;is module already loaded? lxi b,modslen call findmo lxi d,dupmsg jnc prabort ;nc is yes ret ;not found, ready to install ; ; CY clear if memory is protected below the bdos. ; set system addresses when checking. ; ; CY set if nothing below BDOS ; CY clear and zero also nothing below ; testlow: lhld 0001h shld wboot$addr xra a mov l,a shld biosbase push h lxi d,-1600h dad d shld ccpbase pop h mvi l,4 mov a,m ; check that no non-permanent module inx h ; is below the ccp mov h,m mov l,a shld wbpointer lda 0002 ;does jmp point below the bios? cmp h ret ;CY clear if something below ; ; NZ if a non-permanent (non-standard) module active below ccp ; e.g. DDT, EX ; testnonperm: lhld wbpointer lxi d,6+2 ;2 jmps, 1 word;point at protect addr dad d mov e,m inx h mov d,m lhld 0006h ;unless the same, this isn't a perm. module ora a dsbc d ret ;NZ if a transient module below ; IF NEEDBG ; ; return CY clear if BG found ; findbg: lxi h,bgstr lxi b,bgstrlen call findmo shld bg$base ret ; bgstr: db 'BG ' ;Backgrounder id string, all versions bgstrlen equ $-bgstr ENDIF ; ; ; findmodule - find semi-permanent resident module ; ; enter: hl = ptr to string (mod. name) to match ; bc = length ; exit: CY clear if found ; findmo: shld matchaddr lhld 1 inx h ;point at wb addr in bios jmp vector findm1: mov a,m ;starhl inx h mov h,m mov l,a push h xchg lhld 1 ;if (hl=bios+4) < addr ora a dsbc d pop h rc ; then not found push h lxi d,Oname-3 dad d ;module name ptr mov e,m ;starde inx h mov d,m matchaddr equ $+1 lxi h,ADDRESS ;does it match request? push b ;save bc for next time call verify pop b pop h jrnz findm2 dcx h ;found it dcx h dcx h ora a ret ;found, CY ; findm2: lxi d,Ouwboot+1-3 ;move up to next module dad d jr findm1 ; entry:hl = 1ststring ; de = 2nd string ; bc = length ; ; exit: NZ if mismatch ; verify: ldax d cci inx d rnz jpe verify ret ; ;-------------------- PAGE ; message handlers ; ; say name of module ; SAYMOD: lxi d,crlf call print lxi h,CODE+Oname ;get module name ptr mov e,m inx h mov d,m lxi h,CODE-200h ;adjust for location in loader ;-100 for prl, -100 for ptr table area dad d call display lxi d,loadmsg jmp print ; ; print nul/8-bit terminated string at HL ; preserves bc DISPLAY: push b ;preserve C disp0: mov a,m rlc srlr a ;shift right logical (register) A inx h jrz disp1 push h push psw call conoua pop psw pop h jrnc disp0 disp1: pop b ret ; use bdos for all output (so ^P works) ; ; 'conout' function when char in A ; conoua: mov e,a jr conou1 conout: mov e,c conou1: mvi c,2 jmp bdos ; SIGNON: lxi d,banner call print lxi d,copyrit print: mvi c,9 jmp bdos ; ; dupmsg: db CR,LF DB 'Module is already loaded.$',0 nonpermmsg: db CR,LF,BELL,'Non-permanent module active!$',0 IF NEEDBG nobgmsg: db CR,LF,BELL,'Backgrounder not installed!$',0 ENDIF loadmsg:db ' loaded.$' copyrit:db 'Copyright (c) 1985 - Plu*Perfect Systems',CR,LF,'$' ; PAGE ;---------------------------------------- INSTALL: ; ;1. Copy the bios jmp vector ; lhld biosbase lxi d,obios lxi b,32h ldir ; assumes module to be loaded has parameters ; at protect address,not higher ;2. calc base addr = CURRENT protect addr-codelength ; inst1: xra a lhld 0006h ;check for protect addr below ccp push h lded ccpbase dsbc d pop h jrc inst1a ;use 6 xchg ;else protect the ccp dcr a ;set ff inst1a: sta gotoccp lded codelen ;length of code + parameter record ora a dsbc d shld param$offset inr h ;allow for 1 page of shld offset ;loader parameters dcr h ; ;3. Relocate module code and move module to final address. ; xchg ;target addr of loader parameters lxi h,MPARAMS call reloc ; ;4. Install standard parameters into module header. ; lhld biosbase lxi d,-BDOSLEN+6 dad d lxi b,1 ;install protect JMP to bdos call setw lhld wboot$addr ;install warmboot addr instwb: lxi b,Owbootaddr call setw lhld offset ;install protect addr lxi b,Oprotect call setw lhld obios+03h+1 ;install upper warmboot addr lda gotoccp ;unless it's the top module ora a jrz instwb1 lhld ccpbase ;in which case install ccp entry mvi l,3 ;addr instwb1:lxi b,Ouwboot+1 ; ; fall thru ; setw: xchg lhld offset ;base address of code dad b mov m,e inx h mov m,d ret ; PAGE ;************************************************************ ; ; Install the specific parameters for this module. ; PATCHIN: ; ; 1. install original bios addresses into module routines ; lbcd param$offset ;bc holds the parameter offset addr ; lhld wbboot ;get offset to module address lded obios+03h+1 ;get orig. bios word call settobios ;install that word into module ; lhld wconstat lded obios+06h+1 call settobios ; lhld wconin lded obios+09h+1 ;conin call settobios ; lhld wconot lded obios+0ch+1 ;conout call settobios ; lhld wlst1 ;list lded obios+0fh+1 call settobios lhld wlst2 ;twice call settobios ; lhld wlsts1 ;liststat lded obios+2dh+1 call settobios lhld wlsts2 ;also twice call settobios ; ; 2. now set bios traps ; ; A warm-boot trap is always needed. ; lhld offset ;warm-boot trap lxi d,Owbootent dad d xchg lhld biosbase mvi l,4 mov m,e inx h mov m,d ; ; This module has just one other trap - LIST. ; lhld wxlist ;offset to module's LIST entry 'xlist' dad b ;addr it xchg lhld biosbase mvi l,0fh+1 ;divert bios list jmp to xlist in module mov m,e inx h mov m,d ; ; 3. Set addresses for removing traps. ; dcx h xchg lhld wxblst ;install bios LIST+1 addr dad b ;in module so 'xremove' mov m,e ;can unset the trap inx h mov m,d ; lhld wxwbadd ;warmboot addr to restore dad b mvi e,04h ;at bios wb jump addr mov m,e inx h mov m,d ; 4. Calculate and save addresses of data areas in module. ; lhld wxdata dad b shld ydata lhld wstrtbl dad b shld ystrtbl lhld wstrngs dad b shld ystrngs ret settobios: dad b mov m,e inx h mov m,d ret PAGE ; ; Move data from configuration area to module. ; Check for bad patch values in process. ; MOVDATA: ;first check that assembly constants in high module match those in ;loader lhld ydata ;start of high module data dcx h mov d,m dcx h mov e,m dcx h mvi a,MAXCHR cmp m ;check max number of chars jnz bad$constants lxi h,MAXSTRS ora a ;clear carry dsbc d ;compare jnz bad$constants lda lchrcnt cpi MAXCHR+1 ;check for overflow jrnc baddata mov c,a mvi b,0 push b inx b inx b inx b ;adjust length of area lxi h,lmethod ;from config. data area lded ydata ;to module data area ldir ;hl -> 1st str len pop b ;c = lchrcnt lded ystrtbl ;->module address table to stack push d lded ystrngs ;->module string area jr ctest lp0: xthl ;->str addr table mov m,e ;install str addr into mod. table inx h mov m,d inx h xthl mov a,m ;get length byte mov b,a stax d ;install it inx h inx d inr b jr btest lp1: mov a,m stax d inx h inx d btest: djnz lp1 ;count out a string ctest: dcr c jrnz lp0 ;count out number of strings ;check length limit lxi h,-MAXSTRS dad d ;last high addr-MAXSTRS xchg ; lhld ystrngs ora a dsbc d ;first addr - (last addr-MAX) pop h ;clear stack rnc baddata: lxi d,badmsg jmp prabort badmsg: db BELL,'Data overflow!',CR,LF,'$' bad$constants: lxi d,badparmmsg jmp prabort badparmmsg: db BELL,'Module Parameters Different from Loader!' db CR,LF,'$' ; ;************************************************************ ; PAGE ; org RELOCPAGE*100H ; ; This code must reside on a page boundary to permit ; debugger to patch in code length to be relocated. ; ; Plu*Perfect Systems relocater for byte-relocatable code ; copyright (c) 1985 ; ; enter: hl=base of code to be relocated ; de=destination ; bc is stuffed with word at codelen ; The CODELEN word in the next instr. is the length of the code ; to be relocated, generated by LINK filename[op] ; The length is at 0001-0002 of the .PRL file. ; We move it here with a debugger such as DDT. ; reloc: CODELEN equ $+1 lxi b,ADDRESS ;generated by the PRL module push b ;save byte count push d ;save destination sded baseaddr ldir ;move the code pop d ;destination= amt to reloc to de dcr d ;adjust for 100h load addr of .PRL file pop b ;get byte count back push h ;after ldir, hl = bit map addr to ix popix di sspd savstk1 ;switch the sp baseaddr equ $+1 lxi sp,ADDRESS ;point at start of code, lag 1 byte dcx sp ;because prl marks the high byte mvi l,0 ;bit counter rloop: mov a,b ;check byte count ora c jrz rdone dcx b ;reduce byte count mov a,l ;need a new map byte ? ani 7 jrnz same ldx h,0 ;get next bit map byte inxix same: mov a,h ;fetch current byte ral ;shift left into CY mov h,a ;put it back jrnc noof ;no offset needed popiy ;get word to relocate dady d ;relocate pushiy ;put it back noof: inx sp ;move to next byte of code inr l ;bump bit counter jr rloop ; rdone: lspd savstk1 ei ret ; savstk1:dw 0 ; PAGE ;***************************************************************** ; module's parameter record (org 0) goes here ; These words are addresses relative to start of the module's code. ; They must be in the same order as they appear in the module. ; org PARAMPAGE*100H MPARAMS: wxwbadd: ds 2 wbboot: ds 2 wconstat: ds 2 wconin: ds 2 wconot: ds 2 wlst1: ds 2 wlst2: ds 2 wlsts1: ds 2 wlsts2: ds 2 wxlist: ds 2 wxblst: ds 2 wxdata: ds 2 wstrtbl: ds 2 wstrngs: ds 2 ; ;---------------------------------------- ; The .PRL module code, followed by bitmap goes here. ; It is org 100h, to be relocated to final address. ; org CODEPAGE*100H CODE: ; end