title 'Tarbell Boot loader module for CP/M 3.0' ; Copyright (c) 1983 Tarbell Electronics ; version 1.2 5-26-83 Edwin E. Smith ; added real time clock support 4-13-83 ; added banked equate for non-banked systems 5-26-83 ; note @nbnks byte must appear in move module true equ -1 false equ 0 banked equ true public ?init,?ldccp,?rlccp,?time extrn ?pmsg,?conin extrn @civec,@covec,@aivec,@aovec,@lovec extrn @cbnk,?bnksl,?bank,@date,@hour,@min,@sec extrn @nbnks maclib z80 bdos equ 5 memenb equ 0ah ;memory manager enable port imask equ 09h ;interupt mask port intvect equ 1 ;rtc interrupt vector ; ; timer equates ; tch0 equ 04h ;timer chan 0 address tch1 equ 05h ;timer chan 1 address tch2 equ 06h ;timer chan 2 address tcmnd equ 07h ;timer command port cntr0 equ 00000000b ;counter 0 cntr1 equ 01000000b ;counter 1 cntr2 equ 10000000b ;counter 2 rlword equ 00110000b ;read/load lsb 1st, msb 2nd. rlhbyte equ 00100000b ;read/load msb only. rllbyte equ 00010000b ;read/load lsb only. cntrlt equ 00000000b ;counter latching operation. binary equ 00000000b ;select binary operation. bcd equ 00000001b ;select bcd operation. mode0 equ 00000000b ;interrupt on terminal count. mode1 equ 00000010b ;programmable one-shot. mode2 equ 00000100b ;rate generator. mode3 equ 00000110b ;square wave rate generator. mode4 equ 00001000b ;software triggered strobe. mode5 equ 00001010b ;hardware triggered strobe. intervl equ 40000 ;this = 20 ms (50 hz period) tintrvl equ 50 ;this = 1 second dseg ; init done from banked memory ?init: mvi a,0ffh ;mask all interupts out imask lxi h,08000h shld @civec shld @covec ; assign console to CRT: lxi h,04000h shld @lovec ; assign printer to LPT: lxi h,02000h shld @aivec shld @aovec ; assign AUX to CRT1: xra a call ?bnksl ;select bank 0 out memenb ;turn on memory manager ; ;if we don't want instant crash we must put a jump at loc. 0008h ;in all banks. ; call pg0init ;setup interupt vector(s) ; ;set timer to stop running ; mvi a,cntr2+rlword+mode2+binary ;timer values out tcmnd ;send it mvi a,1 ;stop timer out tch2 dcr a out tch2 in imask ;clear interrupt latch ; ;set up timer and interrupt controller ;this routine is called once ; timint: mvi a,cntr2+rlword+mode2+binary lxi b,intervl ;timer interval out tcmnd ;send init byte mov a,c ;get low byte of count out tch2 ;send it mov a,b ;get high byte of count out tch2 ;send it mvi a,0fdh ;mask all but vi1 out imask ;clear intrp mask bits ; ;now we can announce ourselves to the world. ; lxi h,signon$msg call ?pmsg ; print signon message ret cseg ; boot loading most be done from resident memory ; This version of the boot loader loads the CCP from a file ; called CCP.COM on the system drive (A:). ; First time, load the A:CCP.COM file into TPA ?ldccp: ; Reload CCP from disk file also ?rlccp: xra a sta ccp$fcb+15 ; zero extent lxi h,0 shld fcb$nr ; start at beginning of file lxi d,ccp$fcb call open ; open file containing CCP inr a jz no$CCP ; error if no file... lxi d,0100h call setdma ; start of TPA lxi d,128 call setmulti ; allow up to 16k bytes lxi d,ccp$fcb call read ; load the thing ret no$CCP: ; here if we couldn't find the file lxi h,ccp$msg call ?pmsg ; report this... call ?conin ; get a response jmp ?ldccp ; and try again ;**************************************************************** ;real time clock interrupt handler and jump vector setup * ;routines must be in common memory. * ;**************************************************************** ; No external clock. ?time: ret ;here is where the time gets updated ticker: push psw ;save the world push h ;...well part of it anyway lxi h,tick ;point to tick counter dcr m ;decrease by 1 jrnz tickdone ;= 1 second yet mvi m,tintrvl ;1 sec elapsed, reset counter ; ;one second has passed so tell the world ; lda @sec ;get current second adi 1 ;do a decimal add daa ;make decimal again sta @sec ;update second counter cpi 60h ;new minute yet ? jrnz tickdone ;nope exit ; ;one minute has passed so update ; xra a sta @sec ;clear seconds lda @min ;get current minute adi 1 ;do a decimal add daa ;make decimal again sta @min ;update minute counter cpi 60h ;new hour yet ? jrnz tickdone ;nope exit ; ;one hour has passed so update ; xra a sta @min ;clear minutes lda @hour ;get current hour adi 1 ;do a decimal add daa ;make decimal again sta @hour ;update hour counter cpi 24h ;new day yet ? jrnz tickdone ;nope exit ; ;it's tomorrow so lets tell the system ; xra a sta @hour ;clear hours lhld @date ;get the current date inx h shld @date ;update day counter ; ;done with updates so lets service the interrupt ; tickdone: in imask ;clear the interupt latch ; ;now restore the registers that we used ; pop h pop psw ei ;ready for the next one ret ; ;tick counter ; tick: db 50 ;50 ticks = 1 second ;**************************************************************** ;initialize the rtc jump vectors in each bank * ;**************************************************************** pg0init: if banked lda @nbnks ;get the number of memory banks else mvi a,1 ;init bank 0 only endif pg0$init$loop dcr a ;a=bank number push psw ;save it call ?bank ;select the bank mvi a,0c3h ;jump instruction sta intvect * 8 ;location 0008h for vector 1 lxi h,ticker ;get address of rtc service routine shld (intvect * 8)+1 ;complete jump instruction pop psw jnz pg0$init$loop ;loop till all banks are setup ; ;we were called from bank 0 so we will return to bank 0 ; ret ;**************************************************************** ; end of real time clock stuff * ;**************************************************************** ; CP/M BDOS Function Interfaces open: mvi c,15 jmp bdos ; open file control block setdma: mvi c,26 jmp bdos ; set data transfer address setmulti: mvi c,44 jmp bdos ; set record count read: mvi c,20 jmp bdos ; read records signon$msg db 13,10,13,10,'Tarbell Electronics' db 13,10,'CP/M Plus Version 3.0 3-15-83',13,10,0 ccp$msg db 13,10,'BIOS Err on A: No CCP.COM file',0 ccp$fcb db 1,'CCP ','COM',0,0,0,0 ds 16 fcb$nr db 0,0,0 end