; code in this module was modified to set the screen up for 80 col ; automaticly, all 40 column code has been deleted. the label NULL40 is ; still maintained in the device table as removing it would change ; the subsequent device numbers, affecting the use of RS-232 (it would ; require rewrites of all modem programs) ; code at the label ?rlccp has been added to reset the feel and repeat ; rate at every warm start, this corrects problems that arise after ; terminating a modem program. ; ; Code has been added to allow setting the cursor flash rate and shape, ; set the temporary drive, and the drive search chain. ; ; Code has been added to require a password to be entered before the ; system finishes initialization, 3 trys are allowed, the system locks ; up if you fail all three. ; ; A label has been added to allow the 6551 init code to install baud ; rates based on the value in DEVTBL. ; ; User assumes _ALL_ liability for the use of this module. Original ; Copyright is still maintained by Commodore ; ; Code to set cursor and drive chain; Copyright April 26, 1987 by ; James W. Waltrip IV ; title 'C128 BIOS, main I/O and sys functions 26 April 87' ; ; This module contains CXIO,CXINIT,CXMOVE and CXTIME. ; maclib cpm3 maclib z80 maclib cxequ maclib modebaud ;********************************************************** extrn repeat ; repeat rate repeat$amt equ 3 extrn delay ; delay before repeat delay$amt equ 24 printnum equ 08 ; printer device to init ; other printer is 010h (16 decimal) pass equ false ; set to 'false' if password ;protection not wanted pass$word macro db 'PASS' ; password is defined here ; ; if using lower case letters ; ; use the HEX values rather than ; ; the characters themselves ; ; ie.-> 061h instead of 'a' endm ;************************************************************* public ?init,?ldccp,?rlccp public ?user,?di$int extrn ?sysint bdos equ 5 extrn @civec,@covec,@aivec,@aovec,@lovec extrn ?bnksl public ?cinit,?ci,?co,?cist,?cost public @ctbl extrn ?kyscn ; Utility routines in standard BIOS extrn ?wboot ; warm boot vector extrn ?pmsg ; print message @ up to 00 ; saves & extrn ?pdec ; print binary number in from 0 to 99. extrn ?pderr ; print BIOS disk error header extrn ?conin,?cono ; con in and out extrn ?const ; get console status extrn @hour,@min,@sec,@date,?bnksl public ?time page ; ; keyboard scanning routine ; extrn ?get$key,?int$cia extrn Fx$V$tbl ; ; links to 80 column display ; extrn ?out80,?int80 extrn ?pt$i$1101,?pt$o$1,?pt$o$2 ; ; bios8502 function routines ; public ?fun65 ; ; ; public X6551$baud extrn ?int65,?in65,?ins65,?out65 extrn @drcha,@drchb,@drchc,@drchd,@tpdrv DSEG ?fun65: sta vic$cmd ; save the command passed in A fun$di$wait: lda RS232$status ani 01000010b ; transmitting or receiving ? jrnz fun$di$wait ; yes, wait for int to clean up di lda force$map ; get current MMU configuration push psw ; save it sta io$0 ; make I/O 0 current lxi d,1 ; D=0, E=1 lxi b,page$1$h outp d dcr c outp e ; page 1, 0-1 dcr c outp d dcr c outp d ; page 0, 0-0 call enable$6502+6 ; go run the 8502 mvi c,low(page$1$h) outp e dcr c outp e ; page 1, 1-1 dcr c outp e dcr c outp d ; page 0, 1-0 pop psw ; recover the MMU config. sta force$map ; restore it ei ; turn interrupts back on lda vic$data ; get command results ora a ; set the zero flag if A=0 ret ?di$int: push psw di$int$1: lda RS232$status ani 01000010b ; transmitting or receiving ? jrnz di$int$1 ; yes, wait for int to clean up pop psw di ret page ; ; set up the MMU for CP/M Plus ; DSEG ; init done from banked memory ?init: mvi a,3eh ; force MMU into I/O space sta force$map ; lxi h,mmu$table+11-1 ; table of 11 values lxi b,mmu$start+11-1 ; to to MMU registers mvi d,11 ; move all 11 bytes to the MMU init$mmu$loop: mov a,m outp a dcx h dcx b dcr d jrnz init$mmu$loop mvi a,1 ; enable track and sector status sta stat$enable ; on the status line ; mvi a,1h ; no parity, 8 bits, 1 stop bit sta XxD$config lxi h,Fx$V$tbl shld key$FX$function ;********************************************************* ; ; install I/O assignments ; lxi h,4000h ; 80 and 40 column drivers shld @covec mvi h,80h shld @civec ; assign console input to keys mvi h,printnum ; device # 4 shld @lovec ; assign printer to LPT: mvi h,00h shld @aivec shld @aovec ; assign rdr/pun port ; ; init SCB ; mvi a,4 ; The following instr. set up the sta @drcha ; Drive Serch Chain, and the Temp. sta @tpdrv ; Drive. This replaces the SETDEF mvi a,1 ; method of setting this up. sta @drchb mvi a,2 sta @drchc mvi a,3 sta @drchd ; ; Where are the bytes for COM/SUB execution? ; ; 80 Col. screen cursor attributes ; lxi b,0D600h ; 80 col cmd reg mvi a,010 ; reg 10 of 8563 (cursor type, start scan line) outp a ; tell 8563 you want reg 10 lx1: inp a ; get status jp lx1 ; loop until status good lxi b,0D601h ; 80 col data reg mvi a,01100011b ; slow cursor, scan line 4 lines from top outp a ; lxi b,0D600h mvi a,011 ; reg 11 of 8563 (cursor end scan line) outp a lx2: inp a jp lx2 lxi b,0D601h mvi a,00000101b ; Scan line 5 lines from top outp a lxi b,0D011h ; Vic controll reg 1 mvi a,00001000b ; turn off Vic outp a ; page ; ; print sign on message ; call prt$msg ; print signon message db 'Z'-'@' ; home and clear screen (to BG color) db esc,esc,esc,21h,esc,esc,esc,30 ; black back, white char db 'Z'-'@' db esc,'=',32+2,32+37,'CP/M 3.0' db esc,'=',32+3,32+31,'On the Commodore 128 ' db esc,'=',32+4,32+22,'6 Dec 85 System -*- Modified 26 Apr 87' db cr,lf,lf,lf,lf,0 ;;********************************************************* page ; ; mvi a,-1 ; set block move to NORMAL mode sta source$bnk ; ; install mode 2 page vectors ; mvi a,JMP sta INT$vector ; install a JMP at vector location lxi h,?sysint shld INT$vector+1 ; install int$handler adr ; ; A software fix is required for the lack of hardware to force the ; LSB of the INT vector to 0. If the bus floats INT VECT could be ; read as 0FFh; thus ADRh=I (I=0FCh) ADRl=FF for first read, and ; ADRh=I+1 ADRl=00 for second, to ensure that control is retained ; 0FD00h will also have FDh in it. ; lxi h,int$block ; FC00h lxi d,int$block+1 ; FC01h lxi b,256-1+1 ; interrupt pointer block mvi m,INT$vector/256 ; high and low are equal (FD) ldir mvi a,INT$block/256 stai ; set interrupt page pointer im2 ; enable mode 2 interrupts page ; ; mvi a,vicinit ; null command just to setup BIOS8502 call ?fun65 ; ; ; lda sys$freq ; 0=60Hz 0FFh=50Hz ani 80h ; 0=60Hz 080h=50Hz mov l,a ; save in L lxi b,cia$1+0eh ; point to CRA inp a ; get old config ani 7fh ; clear freq bit ora l ; add in new freq bit outp a ; set new config mvi c,8 ; start RTC outp a lxi h,date$hex shld @date ; set date to system data ; ; setup the sound variables ; lhld key$tbl lxi d,58*4 dad d mov e,m inx h mov d,m inx h xchg shld sound1 ; H=SID reg 24, L=SID reg 5 xchg mov e,m inx h mov d,m xchg shld sound2 ; H=SID reg 6, L=SID reg 1 lxi h,9 dad d mov e,m inx h mov d,m xchg shld sound3 ; H=SID reg 4 then L=SID reg 4 ; ; set-up key click sound registers ; lxi b,sid+7 lxi h,0040h outp l ; (sid+7)=40h inr c outp l ; (sid+8)=40h mvi c,low(sid+12) outp h ; (sid+12)=0 Attack=2ms, Decay=6ms inr c outp h ; (sid+13)=0 Sustain=0, Release=6ms mvi a,6 sta tick$vol ; set keyclick volumn level ;********************************************** ; Password protection routine ;********************************************** ; if pass ; mvi a,0 sta count loop1: lda count inr a sta count cpi 4 jnz over call prt$msg db cr,lf,lf,esc,'G2','Password Error, System Aborting....',0 there: jmp there over: lxi h,cmp$string push h call prt$msg db cr,lf,'Password: ',0 lz1: call key$board$in pop h cmp m jnz loop1 inx h push h mov a,m cpi 0 jnz lz1 call prt$msg db cr,lf,lf,0 ; endif ; ret ; if pass ; cmp$string: pass$word ;password macro db 0 count: ds 1 ; endif ; ;*************************************************** ; mmu$table: mmu$tbl$M page ; ; ; CSEG prt$msg: xthl call ?pmsg xthl ret ; ; placed in common memory to keep IO from stepping on this code ; always called from bank 0 ; CSEG read$d505: sta io$0 ; enable MMU (not RAM) lxi b,0d505h inp a ; read 40/80 column screen sta bank$0 ; re-enable RAM ret page ; ; ; DSEG init$RS232: di xra a sta RS232$status lxi h,RxD$buf$count ; clear the count mvi m,0 inr l ; point to RxD$buf$put mvi m,low(RxD$buffer) inr l ; point to RxD$buf$get mvi m,low(RxD$buffer) lxi h,NTSC$baud$table ;*************************************** ; code to check for NTSC or PAL removed here, system now assumes ; that you are using a north american 128 at 60 hz. ; *************************************** use$NTSC: lda RS232$baud cpi baud$1200 ; baud rate less then 1200 baud jrc baud$ok ; yes, go set it mvi a,baud$1200 ; no, 1200 baud is the max sta RS232$baud ; (change to 1200 baud) baud$ok: mov e,a mvi d,0 dad d ; +1X dad d ; +1X dad d ; +1X = +3X mov e,m inx h mov d,m inx h ; mov a,m ; get rate # sta int$rate ; lxi b,CIA1+timer$b$low ; outp e ; inr c ; point to timer$b$high outp d ; mvi a,11h ; mvi c,CIA$ctrl$b ; turn on timer B outp a ; lxi b,CIA2+data$b ; setup user port for RS232 inp a ; get old data ori 6 ; set CTS and DTR outp a ; update it ei ret page ; ; NTSC rates (1.02273 MHz) ; NTSC$baud$table: dw 6818 ; no baud rate (6666.47) db 1 dw 6818 ; 50 6666.7us (6666.47) db 1 dw 4545 ; 75 4444.4us (4443.99) db 1 dw 3099 ; 110 3030.3us (3030.13) db 1 dw 2544 ; 134 2487.6us (2487.46) db 1 dw 2273 ; 150 2222.2us (2222.48) db 2 dw 1136 ; 300 1111.1us (1110.75) db 3 dw 568 ; 600 555.6us ( 555.38) db 6 dw 284 ; 1200 277.8us ( 277.69) db 12 ;**************************************************** ; PAL baud table removed from here (redundant, as it is not checked for ; or used) ;****************************************************** page ; ; ; out$RS232: lda RS232$status ani 80h jrnz out$RS232 mov a,c sta xmit$data ; get character to send in A lxi h,RS232$status setb 7,m ; set Xmit request bit ret ; ; ; out$st$RS232: lda RS232$status ani 80h ; bit 8 set if busy xri 80h ; A cleared if busy (=80h if not) rz ori 0ffh ; A=ff if ready (not busy) ret ; ; ; in$RS232: lda RS232$status ani 1 jrz in$RS232 lda recv$data lxi h,RS232$status res 0,m ret ; ; ; in$st$RS232: lda RS232$status ani 1 rz ori 0ffh ; set data ready (-1) ret page ; ; this routine is used to provide the user with a method ; of interfacing with low level system functions ; CSEG ; ; input: ; all registers except HL and A are passed to function ; ; output: ; all resisters from function are preserved ; ?user: shld user$hl$temp xchg shld de$temp ; save DE for called function mov e,a ; place function number in E mvi a,num$user$fun-1 ; last legal function number call vector ; function usr$tb: dw read$mem$0 ; 0 dw write$mem$0 ; 1 dw ?kyscn ; 2 dw do$rom$fun ; 3 (L=function #) dw do$6502$fun ; 4 (L=function #) dw read$d505 ; 5 returns MMU reg in A dw code$error ; not 0 to 5 ret version number in HL num$user$fun equ ($-usr$tb)/2 page ; ; address in DE is read and returned in C ; A=0 if no error ; DSEG read$mem$0: ldax d ; read location addressed by DE mov c,a ; value returned in C xra a ; clear error flag ret ; ; address in DE is written to with value in C ; A=0 if no errors ; write$mem$0: mvi a,-1 ; get error flag and 0ffh value cmp d ; do not allow write from FF00 to FFFF ; this is 8502 space, MMU direct reg. rz mov a,d cpi 10h ; do not allow write from 0000 to 0FFF ; this is ROM space mvi a,-1 ; get error flag rc ; return if 00h to 0fh mov a,c stax d xra a ; clear error flag ret page ; ; This is the function code entry point for direct execution ; of driver functions. If the MSB of the function number is ; set, the 40 column driver is used; else the 80 column drive ; is used. ; do$rom$fun: lhld user$hl$temp ; get HL (L=fun #) mvi a,7eh ; only allow even functions ana l cpi 79h jrc no$hl$req lhld @dma ; HL will be passed in @dma by push h ; ..the user no$hl$req: mov l,a rst 5 ; call rom functon (RCALL) L=fun # ret ; mvi a,7eh ; only allow even functions ; ana l ; sta no$hl$req+1 ; cpi 79h ; jrc no$hl$req ; lhld @dma ; HL will be passed in @dma by ; push h ; ..the user ;no$hl$req: ; will be changed to RCALL xx RET for next release (ROM FN 7A, 7C ; and 7E will not function with current code, they expect ; a return address on the stack ; ; RJMP 5Eh ; unused function, real fun# installed ; ..above do$6502$fun: lhld user$hl$temp mov a,l jmp ?fun65 ; ; ; code$error: lxi h,date$hex mvi a,-1 ret page ; ; ; CSEG ?rlccp: ; ;****************************************************************** mvi a,01 ; sets feel, put here to set it sta 0fd52h ; on every warm start mvi a,repeat$amt ; set at 3 sta repeat mvi a,delay$amt ; set at 24 sta delay ;****************************************************************** lxi h,ccp$buffer lxi b,0c80h load$ccp: sta bank$0 mov a,m sta bank$1 lxi d,-ccp$buffer+100h dad d mov m,a lxi d,ccp$buffer-100h+1 dad d dcx b mov a,b ora c jrnz load$ccp ret page ; ; ; CSEG ?ldccp: 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 jrz 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 lxi h,0100h lxi b,0c80h lda force$map push psw ; ; save$ccp: sta bank$1 mov a,m sta bank$0 lxi d,ccp$buffer-100h dad d mov m,a lxi d,-ccp$buffer+100h+1 dad d dcx b mov a,b ora c jrnz save$ccp pop psw sta force$map ret page ; ; ; no$CCP: ; here if we couldn't find the file call prtmsg ; report this... db cr,lf,'BIOS Err on A: No CCP.COM file',0 call ?conin ; get a response jr ?ldccp ; and try again ; ; CP/M BDOS Function Interfaces ; CSEG open: mvi c,15 ; open file control block db 21h ; lxi h,(mvi c,26) setdma: mvi c,26 ; set data transfer address db 21h ; lxi h,(mvi c,44) setmulti: mvi c,44 ; set record count db 21h ; lxi h,(mvi c,20) read: mvi c,20 ; read records jmp bdos ; 12345678901 ccp$fcb db 1,'CCP COM',0,0,0 fcb$rc db 0 ds 16 fcb$nr db 0,0,0 page ; ; CXIO.ASM and CXEM.ASM ; ;========================================================== ; ROUITINE TO VECTOR TO HANDLER ;========================================================== ; CP/M IO routines b=device : c=output char : a=input char ; CSEG ;********************************************************************* ; code in the following vector tables has been altered to null the 40 ; column screen ;-------------------------------------------------- ?cinit: ; initialize usarts mov b,c call vector$io ; jump with table adr on stack number$drivers: dw ?int$cia ; keys dw ?int80 ; 80col dw rret ; 40col dw ?pt$i$1101 ; prt1 dw ?pt$i$1101 ; prt2 dw ?int65 ; 6551 dw init$RS232 ; software RS232 dw rret ; max$devices equ (($-number$drivers)/2)-1 ; ; ; ?ci: ; character input call vector$io ; jump with table adr on stack dw key$board$in ; keys dw rret ; 80col dw rret ; 40col dw rret ; ptr1 dw rret ; prt2 dw ?in65 ; 6551 dw in$RS232 ; software RS232 dw null$input ; ; ; ?cist: ; character input status call vector$io ; jump with table adr on stack dw key$board$stat ; keys dw rret ; 80col dw rret ; 40col dw rret ; prt1 dw rret ; prt2 dw ?ins65 ; 6551 dw in$st$RS232 ; software RS232 dw rret ; ; ; ?co: ; character output call vector$io ; jump with table adr on stack dw rret ; keys dw ?out80 ; 80col dw rret ; 40col dw ?pt$o$1 ; prt1 dw ?pt$o$2 ; prt2 dw ?out65 ; 6551 dw out$RS232 ; software RS232 dw rret ; ; ; ?cost: ; character output status call vector$io ; jump with table adr on stack dw ret$true ; keys dw ret$true ; 80col dw ret$true ; 40col dw ret$true ; prt1 ?pt$s$1101 dw ret$true ; prt2 dw ret$true ; 6551 dw out$st$RS232 ; software RS232 dw ret$true ;************************************************************ page ; ; This entry does not care about values of DE ; vector$io: mvi a,max$devices ; check for device # to high mov e,b ; get devive # in E ; ; ; INPUT: ; Vector # in E, Max device in A ; passes value in DE$TEMP in DE ; HL has routine's address in it on entering routine ; ; OUTPUT: ; ALL registers of returning routine are passed ; vector: pop h ; get address vector list mvi d,0 ; zero out the MSB cmp e ; is it too high? jrnc exist ; no, go get the handler address mov e,a ; yes, set to max$dev$handler(last one) exist: dad d ; dad d ; point into table mov a,m inx h mov h,m mov l,a ; get routine adr in HL shld hl$temp ; save exec adr lxi h,0 dad sp lxi sp,bios$stack push h ; save old stack lhld de$temp xchg lhld hl$temp ; recover exec adr lda force$map ; get current bank push psw sta bank$0 ; set bank 0 as current call ipchl sta a$temp ; save value to return pop psw sta force$map ; set old bank back lda a$temp ; recover value to return shld hl$temp pop h ; recover old stack sphl ; set new stack lhld hl$temp ret ipchl: pchl ; jmp to handler ds 30h bios$stack: page ;========================================================== ; CHARACTER INPUT ROUTINES ;========================================================== DSEG ; ; ; key$board$in: call key$board$stat ; test if key is available jrz key$board$in lda key$buf push psw ; save on stack xra a ; clear key sta key$buf pop psw ; recover current key rret: ret ; ; ; null$input: ; return a ctl-Z for no device mvi a,1Ah ret page ;========================================================== ; CHARACTER DEVICE INPUT STATUS ;========================================================== DSEG ; ; ; key$board$stat: lda key$buf ora a jrnz ret$true call ?get$key ora a ; =0 if none rz ; return character not advailable sta key$buf ; was one, save in key buffer ret$true: ori 0ffh ; and return true ret page cseg @ctbl db 'KEYBRD' ; device 0, internal keyboard db mb$input db baud$none db '80-COL' ; device 1, 80 column display db mb$output db baud$none db 'NULL40' ; device 2, 40 column display db mb$output db baud$none db 'PRT-LQ' ; device 3, serial bus printer (device 4) db mb$output db baud$none db 'PRT-MX' ; device 4, serial bus printer (device 5) db mb$output db baud$none db '6551 ' ; device 5, EXT CRT db mb$in$out+mb$serial+mb$softbaud+mb$xonxoff X6551$baud: db baud$9600 db 'RS-232' ; device 6, software RS232 device db mb$in$out+mb$serial+mb$xonxoff+mb$softbaud RS232$baud: db baud$300 db 0 ; mark end of table page ; ; TIME.ASM ; cseg ; ; HL and DE must be presevered ; ?time: inr c lxi b,cia$hours jrz set$time ; ; update SCB time (READ THE TIME) ; inp a ; read HR (sets sign flag) jp is$am ; jmp if AM (positive) ani 7fh adi 12h ; noon=24(PM), midnight=12(AM) daa cpi 24h ; check for noon (12+12 PM) jrnz set$hr mvi a,12h jr set$hr is$am: cpi 12h ; check for midnight (AM) jrnz set$hr xra a ; becomes 00:00 set$hr: sta @hour mov b,a lda old$hr mov c,a mov a,b sta old$hr cmp c ; if @hour