title 'CCP/M-86 Loader Program and BIOS' ;***************************************************** ; The Loader consists of the three modules: ; Loader BDOS (the file LBDOS.H86), the Loader ; Program and the Loader BIOS. This ; module contains both the Loader Program ; and the Loader BIOS. ; ; The Loader resides in sectors 2-8 on the first track ; of an IBM PC floppy diskette. The Loader is ; is brought into memory by the Boot Sector which ; resides in sector 1 of track 0. The Boot Sector ; is brought into memory by the IBM PC's ROM ; monitor. ; ; The Loader Program opens the file 'CCPM.SYS' using the ; Loader BDOS and Loader BIOS, ; and then reads it into memory. The DS register is set ; to the start of the CCPM DATA area, and a JMPF to the ; first byte of the CCPM code is executed. ; ; The first 128 byte record of the CCPM.SYS file is a header ; with the following format: ; ; +----+----+----+----+----+----+----+----+----+ ; |TYPE| LEN | ABS | MIN | MAX | ; +----+----+----+----+----+----+----+----+----+ ; ; type rb 1 ;seg type ; len rw 1 ;length ; abs dw 1 ;absolute segment address for LOADER ; min rw 1 ;minimum mem ; max rw 1 ;max mem needed ; ; The code is expected first and then the data within CCPM.SYS ; This header record is constructed automatically by the ; GENCCPM utility. See the variables declared at 'SEC1:' ; where the first sector of CCPM.SYS will be read. ; ; Loader may be read into any segment by the Boot ; Sector that does not overlap the area of memory ; the system image in CCPM.SYS will occupy. ; ;***************************************************** false equ 0 true equ not false cr equ 0dh lf equ 0ah ldbdos_offset equ 0000H ;offset of Loader BDOS ldr_offset equ 0900h ;offset of Loader BIOS codetype equ 1 ; code type CMD header datatype equ 2 ; data type CMD header ; bdos function numbers seldskf equ 14 openf equ 15 readsf equ 20 setdmaf equ 26 setuserf equ 32 setmultcntf equ 44 setdmabf equ 51 ;***************************************************** ;* ;* CCPMLDR starts here ;* ;***************************************************** cseg org ldr_offset jmp init jmp entry jmp loadp ldusr db 0 ;load user is 0 loadp: ; loader entry from BDOS init ;----- ; entry: CH = boot user number ; CL = boot disk number ; DS,ES,SS = CS mov dx,offset signon call msg mov dl,ldusr mov cl,setuserf ! int 224 ;set user number mov dx,offset ccpmfcb mov cl,openf ! int 224 ;open CCPM.SYS file cmp al,255 ! jne perr ;insure good file mov dx,offset nofile jmp stop perr: mov dx,ds mov cl,setdmabf ! int 224 ;set DMA segment address mov dx,offset sec1 mov cl,setdmaf ! int 224 ;set DMA offset address mov dl,1 mov cl,setmultcntf ! int 224 ;set Multi-sector count to 1 mov dx,offset ccpmfcb mov cl,readsf ! int 224 ; read first record ; the following ; "commented out" code ; can be used to ; perform error checking ; cmp ctype,codetype ; code should be first ; jnz badhdr ; cmp dtype,datatype ; then data ; jnz badhdr ; mov ax,cldseg ; code abs + code length ; add ax,clen ; should be = to data abs ; cmp ax,dldseg ! jnz badhdr ; add ax,dlen ! cmp ax,cldseg ; check for wrap around ; ja hdrok ;badhdr: ; mov dx,offset rerr ; jmp stop ;hdrok: mov dx,offset csegment call msg ; put memory map on console mov ax,cldseg call phex ; print base code segment mov dx,offset dsegment call msg ; print base data segment mov ax,dldseg call phex mov dx,128 mov cl,setmultcntf ! int 224 ; set multi-sector count to 128 mov dx,0 mov cl,setdmaf ! int 224 ; set DMA offset to 0 mov dx,cldseg ; initial DMA segment readit1: push dx ; save dma segment mov cl,setdmabf ! int 224 ; set DMA segment for disk IO mov dx,offset ccpmfcb mov cl,readsf ! int 224 ; next 128 sector read pop dx ; restore dma segment add dx,8*128 ; increment dma segment cmp al,01H ! je done ; check for EOF cmp al,0 ! je readit1 ; check for good write mov dx,offset rerr ; print READ ERROR message jmp stop done: call pcrlf ; and a crlf mov clen,0 ; set CCPM offset to 0 mov ds,dldseg ; CCP/M data segment jmpf cs:dword ptr clen ; leap to CCPM initialization ;***************************** ;* ;* subroutines ;* ;***************************** stop: ;---- call msg cli ! hlt phex: ;print 4 hex characters from ax ;---- mov cx,0404h ; 4 in both CH and CL lhex: rol ax,cl ; rotate left 4 push cx ! push ax ; save crucial registers call pnib ; print hex nibble pop ax ! pop cx ; restore registers dec ch ! jnz lhex ; and loop four times ret pnib: ;print low nibble in AL as hex char and al,0fh ! cmp al,9 ja p10 ;above 9 ? add al,'0' ;digit jmps prn p10: add al,'A'-10 ;char a-e prn: mov dl,al putchar: ;------- mov cl,dl jmp io_conout pcrlf: ;----- mov dx,offset crlf ;print carriage return, line feed msg: ;print msg starting at dx until $ ;--- mov bx,dx msg1: mov dl,[bx] cmp dl,'$' ! je msg2 push bx call putchar pop bx inc bx jmps msg1 msg2: ret eject ;************************************************************************ ;* ;* ;* L O A D E R ;* ;* B I O S - 8 6 ;* ============= ;* ;* CP/M-86 3.0 or Concurrent CP/M-86 2.0 ;* ;* Boot Loader I/O System ;* for the ;* IBM Personal Computer ;* ;* Copyright (c) 1982 ;* Digital Research, Inc. ;* box 579, Pacific Grove ;* California, 93950 ;* ;* (Permission is hereby granted to use or ;* abstract the following program in the ;* implementation of Concurrent CP/M-86, ;* CP/M, MP/M or CP/Net for the 8086 or 8088 ;* micro-processor.) ;* ;************************************************************************ ;* ;* Register usage for BIOS interface routines: ;* ;* Entry: AL = function # (in entry) ;* CX = entry parameter ;* DX = entry parameter ;* DS = LDBDOS data segment ;* ;* Exit: AX = return ;* BX = AX (in exit) ;* ALL SEGMENT REGISTERS PRESERVED: ;* CS,DS,ES,SS must be preserved though call ;* ;* Notes: flag set and the far jump to the dispatcher are ;* the only legal Operating System "entry" points ;* for an interrupt routine) ;* ;* changes have been made in the ;* register conventions from ;* the CP/M-86 BIOS and the MP/M-86 BIOS. ;* ;************************************************************************ ;************************************************************************ ;* * ;* IBM PC SOFTWARE INTERRUPT STRUCTURE * ;* * ;************************************************************************ tick_interrupt equ 08h keyboard_interrupt equ 09h disk_interrupt equ 0Eh os_interrupt equ 224 ;Loader BDOS entry debugger_interrupt equ 225 ;debugger entry to O.S. ;************************************************************************ ;* * ;* INTERFACE TO LOADER BDOS * ;* * ;************************************************************************ ;===== ;===== entry: ;arrive here from JMP at ;===== ;03H in BIOS code segment ;===== ; entry: AL = function number ; CX, DX parameters ; exit: AX = BX = return ; ALL SEGMENT REGISTERS PRESERVED: ; CS,DS,ES,SS must be preserved though call ; Note: no alteration of stack is allowed during entry except ; for the return address caused by the "call function_table[bx]" ; instruction. cld ;set the direction flag xor ah,ah shl al,1 ;multiply by 2 mov bx,ax ;put in indirect register call function_table[bx] ;no range checking needed mov bx,ax ;only called by loader BDOS retf ;return to loader BDOS io_ret: ret ;------ i_disk: ;------ mov al,pic_nseoi ;signal end of interrupt out pic_even_port,al ;to 8259 iret ;note we destroyed AL ;========= io_conout: ;========= ; entry: CL = character to output ; DL = device number ; exit: None ; ALL SEGMENT REGISTERS PRESERVED: ; CS,DS,ES,SS must be preserved though call ; Put character in screen and update cursor position ; BX = screen structure mov bx,offset ss0 mov di,ss_cursor[bx] ;cursor offset in bytes cmp cl,cr je carriage_return cmp cl,lf je linefeed push es mov ax,bw_video_seg mov es,ax ;segment of foreground screen mov al,cl mov ah,07h ;default attribute stosw ;update and don't touch the attribute pop es ;DI incremented by 2 cmp ss_column[bx],columns_per_screen - 1 jne inc_col call carriage_return jmp line_feed inc_col: inc ss_column[bx] ;DI = next data, attribute mov ss_cursor[bx],di ;save new cursor position ret ;--------------- carriage_return: ;--------------- ; entry: BX = screen structure ; exit: BX preserved xor dx,dx ;AX = 0 xchg dl,ss_column[bx] ;set cursor position to begining of shl dx,1 ;times 2 for data and attribute sub ss_cursor[bx],dx ;set new cursor position ret ;--------- line_feed: ;--------- ; entry: BX = screen structure ; exit: BX preserved ; This routine assumes the loader will never ; need to scroll the screen inc ss_row[bx] add ss_cursor[bx],columns_per_screen*2 ret ;************************************************************************ ;* * ;* 6845 CRT CONTROLLER PORT AND COMMAND EQUATES * ;* * ;************************************************************************ ; The IBM PC's monochrome memory mapped video display begins ; at paragraph 0B000H. It represents a screen 80 X 25. ; Each video character requires a word value, the low byte ; is the ASCII code (characters codes > 128 are also displayed) ; and the high byte is an attribute byte. The 25th line ; is reserved by this BIOS as a status line. bw_card equ 003b4h video_on equ 00029h video_off equ 00021h cursor_start equ 10 cursor_end equ 11 display_start_hi equ 12 display_start_low equ 13 cursor_hi equ 14 cursor_low equ 15 light_pen_hi equ 16 light_pen_low equ 17 ;************************************************************************ ;* * ;* SCREEN PARAMETERS * ;* * ;************************************************************************ rows_per_screen equ 24 columns_per_screen equ 80 screen_siz equ rows_per_screen * columns_per_screen ;in words bw_video_seg equ 0b000h ;segment address of ;start of video ram bw_video_status_line equ screen_siz * 2 ;byte offset of status line ;************************************************************************ ;* * ;* SCREEN STRUCTURES * ;* * ;************************************************************************ ; Each virtual console has a structure of the following ; format associated with it. (SS = Screen Structure) ; The data in this structure is dependent on the type of screen ; supported and any escape sequence handling in io_conout. ; Note: ss_cursor, ss_row, ss_column are relative to 0 and are ; word pointers, i.e., if ss_cursor is 1 then it refers to ; bytes 2 and 3 in the video RAM or a background screen's ; data area. ss_cursor equ word ptr 0 ;points at data/attrib ss_escape equ word ptr ss_cursor + word ;escape routine to return to ss_screen_seg equ word ptr ss_escape + word ;data for screen image ss_row equ byte ptr ss_screen_seg + word ;current row ss_column equ byte ptr ss_row + byte ;current col ; DISK I/O ; -------- ;************************************************************************ ;* * ;* 8237 DIRECT MEMORY ACCESS CONTROLLER PORT AND COMMANDS * ;* * ;************************************************************************ dma_c0_address equ 000h ;8237 channel 0 address rw dma_c0_count equ 001h ;8237 channel 0 transfer count rw dma_c1_address equ 002h ;8237 channel 1 address rw dma_c1_count equ 003h ;8237 channel 1 transfer count rw dma_c2_address equ 004h ;8237 channel 2 address rw dma_c2_count equ 005h ;8237 channel 2 transfer count rw dma_c3_address equ 006h ;8237 channel 3 address rw dma_c3_count equ 007h ;8237 channel 3 transfer count rw dma_stat_reg equ 008h ;8237 status register ro dma_cmd_reg equ dma_stat_reg ;8237 command register wo dma_requ_reg equ dma_stat_reg+1 ;8237 software dma request wo dma_bmsk_reg equ dma_stat_reg+2 ;8237 binary channel mask wo dma_mode_reg equ dma_stat_reg+3 ;8237 mode register wo dma_cbpf equ dma_stat_reg+4 ;8237 clear byte pointer f/f wo dma_temp_reg equ dma_stat_reg+5 ;8237 temporary register ro dma_clear equ dma_stat_reg+5 ;8237 master clear wo dma_mask_reg equ dma_stat_reg+7 ;8237 linear channel mask wo dma_page_c1 equ 080h ;a16 to a20 for channel 1 dma_page_fdc equ 081h ;a16 to a20 for channel 2 dma_page_c3 equ 082h ;a16 to a20 for channel 3 ; The following labels define single mode, address increment ; auto-initialization disable, read or write using channel 2 dma_mode_write_fdc equ 01001010b dma_mode_read_fdc equ 01000110b dma_bmsk_fdc equ 00000010b ;binary channel mask for disk ; DMA channel assignments ;channel 0 dynamic memory refresh ;channel 1 ;channel 2 floppy disk controller ;channel 3 ;************************************************************************ ;* * ;* FLOPPY DISK DRIVER EQUATES * ;* * ;************************************************************************ ; The following equates are set to the size of a double density, ; single sided 5 & 1/4" floppy. bytes_per_sector equ 512 sectors_per_track equ 8 ;1 to 8 bytes_per_track equ sectors_per_track * bytes_per_sector tracks_per_disk equ 40 ;0 to 39 bytes_per_disk equ tracks_per_disk * bytes_per_track ; The following equates are for the INTEL 8272 Floppy Disk ; Controller. fdc_stat equ 03f4h ;status port for the disk controller fdc_data equ fdc_stat+1 ;data port for the disk controller fdc_port equ 03f2h ;all bits clear on channel reset ;7 6 5 4 3 2 1 0 ;| | | | | | \_/ ;| | | | | | | ;| | | | | | drive select 00=a,01=b,10=c,11=d ;| | | | | fdc reset* ;| | | | int & dmarq enable ;d c b a motor on fdc_on equ 00001100b ;mask to keep the 8272 unreset fdc_no_motor equ 11111100b ;mask for no motors fdc_read_cmd equ 01100110b ;mfm, skip deleted data, read fdc_write_cmd equ 01000101b ;mfm, write fdc_format_cmd equ 01001101b ;mfm, format fdc_seek_cmd equ 00001111b ;seek fdc_recal_cmd equ 00000111b ;home to track 0 fdc_si_cmd equ 00001000b ;sense interupt status fdc_spec_cmd equ 00000011b ;specify fdc_ready equ 10000000b ;mask for transfer ready fdc_spec_1 equ 11001111b ;srt=0c, hd unload=0f first specify byte fdc_spec_2 equ 00000011b ;hd load=1, mode=DMA second specify byte f_bytes equ 2 ;magic number for 512 bytes per sector f_sectors equ 8 ;sectors per track f_gap equ 03ah ;magic number for format gap f_filler equ 0e5h ;fill character r_bytes equ 2 ;magic number for 512 bytes r_sectors equ 8 r_gap equ 02ah r_dtl equ 0ffh ; Equates for paramter passing for read and write from the ; BDOS. ; At the disk read and write function entries, ; all disk I/O parameters are on the stack. ; The stack at these entries appears as ; follows: ; ; +-------+-------+ ; +14 | DRV | MCNT | Drive and Multi sector count ; +-------+-------+ ; +12 | TRACK | Track number ; +-------+-------+ ; +10 | SECTOR | Physical sector number ; +-------+-------+ ; +8 | DMA_SEG | DMA segment ; +-------+-------+ ; +6 | DMA_OFF | DMA offset ; +-------+-------+ ; +4 | RET_SEG | BDOS return segment ; +-------+-------+ ; +2 | RET_OFF | BDOS return offset ; +-------+-------+ ; SP+0 | RET_ADR | Return address to BIOS ENTRY routine ; +-------+-------+ ; ; These parameters may be indexed and modifided ; directly on the stack by the BIOS read and write rotines ; They will be removed by the BDOS when the BIOS completes ; the read/write function and returns to the BDOS. drive equ byte ptr 14[bp] mcnt equ byte ptr 15[bp] track equ word ptr 12[bp] sector equ word ptr 10[bp] dma_seg equ word ptr 8[bp] dma_off equ word ptr 6[bp] ; Some equtes in the Disk Parameter Header (DPH) ; and the Disk Parameter Block. xlt equ 0 ;translation table offset in DPH dpb equ 8 ;disk parameter block offset in DPH spt equ 0 ;sectors per track offset in DPB psh equ 15 ;physical shift factor offset in DPB ;************************************************************************ ;* * ;* DISK DRIVER ROUTINES * ;* * ;************************************************************************ ;========= io_seldsk: ; Function 7: Select Disk ;========= ; entry: CL = disk to be selected ; DL = 00h if disk has not been previously selected ; = 01h if disk has been previously selected ; exit: AX = 0 if illegal disk ; = offset of DPH relative from ; BIOS Data Segment ; ALL SEGMENT REGISTERS PRESERVED: ; CS,DS,ES,SS must be preserved though call xor ax,ax ;get ready for error(s) cmp cl,0 ;is it a A: ? jne sel_ret ;if not just exit mov ax,offset dph0 sel_ret: ret ;======= io_read: ; Function 11: Read sector ;======= ; Reads the sector on the current disk, track and ; sector into the current DMA buffer. ; entry: parameters on stack ; exit: AL = 00 if no error occured ; AL = 01 if an error occured ; AL = 0ffh if density change detected ; ALL SEGMENT REGISTERS PRESERVED: ; CS,DS,ES,SS must be preserved though call mov bp,sp ;set BP for indexing into IOPB ; mov dma_mode_storage,dma_mode_read_fdc ; mov fdc_rw_cmd,fdc_read_cmd ;loader always reads call flp_io mov al,1 jnz i_read_ret ;zero flag is set with successful dec al ;I/O i_read_ret: ret flp_io: ;------ ; entry: parameters on stack ; exit: zero flag set if no error mov ax,sectors_per_track;max sectors - starting sector = sub ax,sector ;sectors left on track xor bh,bh mov bl,mcnt ;multi sector count is byte variable cmp ax,bx ;sectors left on track, sectors requested jbe flp_track ;transfer to end of track mov al,mcnt ;transfer to before end of track flp_track: sub mcnt,al ;AL = # sectors to R/W on this iteration ;through FLP_IO, mov track_mcnt,al ;sectors to R/W on current track ;check for 64K page overlap mov ah,al ;shl al,8 (* 256) xor al,al shl ah,1 ;* 512 push ax ;how many bytes to R/W on cur trk mov ax,dma_seg ;compute new 20 bit DMA addr mov bx,dma_off call comp_dma ;returns AX = low 16 bits of 20 bit adr not ax ;how many bytes left in this 64K page pop bx ;BX=bytes to R/W on current track cmp ax,bx ;does this transfer fit in 64K page jb flp_end_64K jmp flp_pg_ok flp_end_64K: ;read to end of 64K page and then ;read spanning sector locally mov al,ah ;how many sectors fit in this page ? xor ah,ah ;bytes left in 64K page shr ax,1 ;divided by 512 test ax,ax ;if 0, no sectors fit jz flp_rwlocal ;in the rest of this 64K page mov num_sec,al ;sectors that fit in end 64K page sub track_mcnt,al ;track_mcnt always > AL call flp_phys_io ;read/write them, DMA already computed jz flp_end64k_ok jmp flp_ret ;return if zero reset on error flp_end64K_ok: xor ax,ax mov al,num_sec ;compute new DMA offset add sector,ax ;still on the same track xchg ah,al ;shl ax,8 (* 256) shl ah,1 ;* bytes_per_sector (512) add dma_off,ax ;64K wrap around is legal flp_rwlocal: ;read into BIOS data segment the spanning mov bx,local_buf ;sector mov ax,ds ;compute 20 bit local DMA addr call comp_dma ; cmp fdc_rw_cmd,fdc_read_cmd ;loader always reads ; ;only need this for writes ; ;reading or writing ? ; je flp_local ; mov si,dma_off ;get the sector to write from local ; mov di,local_buf ;buffer ; push es ! push ds ! push ds ! pop es ; mov ax,dma_seg ! mov ds,ax ; mov cx,bytes_per_sector/2 ; rep movsw ; pop ds ! pop es ; mov dma_off,si ;update DMA offset ; ;flp_local: mov num_sec,1 ;read/write one sector call flp_phys_io jnz flp_ret ;return zero flag reset inc sector ! dec track_mcnt ; cmp fdc_rw_cmd,fdc_read_cmd ;loader always reads ; jne flp_local_done mov di,dma_off ;move the sector to user's area mov si,local_buf ;if reading push es ! mov es,dma_seg mov cx,bytes_per_sector/2 rep movsw mov dma_off,di ;update DMA offset pop es flp_local_done: mov ax,dma_seg ;compute new 20 bit DMA addr mov bx,dma_off ;for next FDC read/write call comp_dma flp_pg_ok: ;read will not cross 64K boundary mov al,track_mcnt ;could be 0 if we just read locally test al,al ! jz nxt_track mov num_sec,al ;read the rest from this track call flp_phys_io ;DMA is already computed jnz flp_ret ;return zero flag reset on error xor ax,ax mov ah,num_sec ;shl num_sec,8 (* 256) shl ah,1 ;* 512 add dma_off,ax nxt_track: xor al,al cmp mcnt,al jz flp_ret ;return successful with zero flag set inc track mov sector,0 jmp flp_io flp_ret: ret comp_dma: ;Compute 20 bit address from offset, segment ;-------- ; entry: AX = segment ; BX = offset ; exit: AX = low 16 bits ; CH = highest 4 bits of address, always less then 16 - ; no megabyte wrap around ; ; The BIOS variables DMA_LOW16 and DMA_HIGH4 are ; set by this routine. These variables are transferred ; to the floppy disk controller by the routine DMA_SET_UP. mov cl,4 ! rol ax,cl ;make paragraphs into bytes mov ch,al ! and al,0f0h ;save high 4 bits, 0 low 4 bits add ax,bx ;add byte offset adc ch,0 ! and ch,0fh ;add in the carry, page is less than mov dma_low16,ax ;16 mov dma_high4,ch ret flp_phys_io: ;----------- ; entry: num_sec = number of sectors to read in this ; operation ; disk parameters on the stack ; exit: zero flag set if ok ; Perform physical actions to read/write floppy diskette mov d_a_number,r_bytes mov d_a_eot,r_sectors mov d_a_gpl,r_gap mov d_a_dtl,r_dtl call motor_and_recal ;make sure motor is on mov recals,max_recals recal_loop: mov retries,max_retries retry_loop: call seek call dma_setup call fdc_read_write jz phys_ret ;if errors dec retries ;attempt retries jnz retry_loop call recal dec recals jnz recal_loop or al,1 ;reset zero flag phys_ret: ;and return error ret motor_and_recal: ;--------------- ; entry: none ; exit: none ; Turn on the motor if off. Also do RECAL operation if ; motor is off. Note: loader does not turn off the motor. mov al,010h ;pick up a bit for motor a mov cl,drive ;fetch the binary drive code shl al,cl ;make it a bit for motor x mov ah,motor_flags ;fetch the motor bits test ah,al ;check to see if its on jnz motor_on_done ;yes then leap or al,fdc_on ;mask in the no reset,enable interupt or al,drive ;mask in the drive mov motor_flags,al ;save for later mov dx,fdc_port ;point to the port number out dx,al ;select & motor on call recal ;back to track zero motor_on_done: ;when we first turn on the motor ret recal: ;----- ; entry: none ; exit: none ; Move the head to home on the selected disk drive. mov disk_arguments,2 ;specify number of arguments mov d_a_command,fdc_recal_cmd mov al,drive ;get current disk mov d_a_drive,al ;set up the command block call fdc_command_put ;go send the command block hlt ;wait for FDC interrupt jmp sense_interrupt ;required after RECAL command ;ret seek: ;---- ; entry: TRACK to seek to on the stack relative to BP ; exit: none mov disk_arguments,3 ;request 3 byte command transfer mov d_a_command,fdc_seek_cmd xor bl,bl ;select head 0 mov ax,track ;get track off stack cmp al,40 ;test for off side 1 jb side_ok ;if single sided then leap mov bl,1 ;else get a head 1 mov ah,79 ;else get back track bias sub ah,al ;compute new track number xchg ah,al ;and get it into al side_ok: mov d_a_head,bl ;set up the head add bl,bl ;multiply by 2 add bl,bl ;multiply by 4 mov d_a_cylinder,al ;set up the track number or bl,drive ;bits 0,1 are drive number mov d_a_drive,bl ;set up disk number mov ax,sector ;get sector off stack inc ax ;sectors are relative to 1 mov d_a_record,al ;and set it up add al,num_sec ;compute new end of track dec al mov d_a_eot,al ;save it call fdc_command_put ;send the command block hlt ;wait for FDC interrupt jmp sense_interrupt ;required after SEEK command ;ret dma_setup: ;--------- ; entry: DMA_MODE_STORAGE, DMA_LOW16, DMA_HIGH4 set up ; exit: none ; Set the DMA device up for a read/write operation. ; The current DMA command word must in DMA_MODE_STORAGE. ; DMA_LOW16 and DMA_HIGH4 are the twenty bit starting address. ; The read/write operation cannot cross a physical 64K boundary. out dma_cbpf,al ;reset the byte pointer mov al,dma_mode_storage ;get the mode byte out dma_mode_reg,al ;set the mode mov ax,dma_low16 ;low 16 bits of 20 bit DMA address out dma_c2_address,al ;send low 8 bits mov al,ah out dma_c2_address,al ;send next 8 bits mov al,dma_high4 ;high 4 bits of 20 bit DMA address out dma_page_fdc,al xor ax,ax mov ah,num_sec ;shl num_sec,8 (* 256) shl ah,1 ;*512 dec ax ;0 relative out dma_c2_count,al ;set up the low byte mov al,ah ;get the low byte out dma_c2_count,al ;and the high byte mov al,dma_bmsk_fdc ;get the binary channel mask out dma_bmsk_reg,al ;enable the disk channel ret fdc_read_write: ;-------------- ; entry: DMA device set up, head positioned on correct ; track ; exit: zero flag set if successful ; Send read or write command to 8272 FDC mov disk_arguments,9 ;9 byte command for read or write mov al,fdc_rw_cmd ;get read or write command mov d_a_command,al ;put it in the command string call fdc_command_put ;send the command to the FDC hlt ;wait for FDC interrupt mov disk_results,7 ;7 byte result transfer call fdc_status_get ;get the result bytes test d_r_st0,0C0H ;test status register 0 ret ;return zero flag set on success sense_interrupt: ;--------------- ; Sense interrupt command on the FDC. It is called ; after a recal or a seek to test for seek complete. mov disk_arguments,1 ;only one byte of command mov d_a_command,fdc_si_cmd ;sense interrupt commmand call fdc_command_put ;send the command to the FDC mov disk_results,2 ;2 bytes are returned call fdc_status_get ;get the 2 result bytes ret fdc_command_put: ;--------------- ; entry: DISK_ARGUMENT array set up ; exit: none ; Send the command block in the DISK_ARGUMENTS table to ; the 8272 FDC. ; The number of commands to write to the FDC is the ; first item in the table. mov dx,fdc_stat ;point to the i/o port mov si,offset disk_arguments ;point to the table of arguments cld ;make sure we go forward lodsb ;get the length of the arguments table mov cl,al ;get it into the count register sub ch,ch ;zero the high byte fdc_command_loop: in al,dx ;get the current control byte test al,fdc_ready ;if not ok to send next byte jz fdc_command_loop ;then loop waiting inc dx ;point at the data port lodsb ;else get the byte to send out dx,al ;send it dec dx ;point back at the status port loop fdc_command_loop ;if not last byte then loop ret ;else were all done fdc_status_get: ;-------------- ; entry: number of results in 1st byte of DISK_RESULTS array ; exit: none ; Get the status information from the 8272 ; FDC and place them in the table at DISK_RESULTS. ; The first byte in the table is the number of results ; to read from the FDC push es ;save UDA mov dx,fdc_stat ;point at the status port mov ax,ds ;get our data segment mov es,ax ;into the extra segment mov di,offset disk_results + 1 ;point to where to put the data cld ;make sure we go forward mov cl,disk_results ;fetch the number of expected results sub ch,ch ;zero the high byte fdc_status_loop: in al,dx ;get the current control byte test al,fdc_ready ;if not ok to read next byte jz fdc_status_loop ;then loop waiting inc dx ;point at the data port in al,dx ;get the byte stosb ;put it in the structure dec dx ;point back at the status port loop fdc_status_loop ;if not last then loop pop es ;restore UDA ret ;else return ; INIT ROUTINE ;************************************************************************ ;* * ;* 8259 PROGRAMMABLE INTERRUPT CONTROLLER COMMANDS * ;* AND PORTS * ;* * ;************************************************************************ pic_even_port equ 020h ;port 0 pic_odd_port equ 021h ;bit 0 is A0 of 8259 instructions pic_nseoi equ 020h ;non specific end of interupt ;************************************************************************ ;* * ;* BIOS INITIALIZATION ROUTINE * ;* * ;************************************************************************ ;track buffer memory_size equ 12h ;ROM interrupt to get memory size equipment equ 11h ;ROM interrupt to get equipment word ; The following routine is used to initialize any required ; data areas and alter any peripheral chip programming when ; starting up CCP/M-86. This code is called once from the ; SUP(ERVISOR) after calling the SUP has called the RTM, ; RTM, CIO, MEM, BDOS initialization routines and before the ; SUP has created the RSP processes. ; This code can be placed in an BIOS data area if the BIOS is ; 8080 model (mixed code and data). Usually, overlaying the ; initialization code with a data area is done after the BIOS ; has been debugged. ;==== ;==== init: ;arrive here from the JMP ;==== ;at 0 in BIOS code segment ;==== cli ! cld ;Supervisor restores DS,ES and int ;224 after on INIT call ; Disk I/O initialization set_up_interrupts: mov al,10111101b ;enable diskette and keyboard out pic_odd_port,al ;interrupts only ;and mask off the rest ; mov disk_arguments,3 ;send specify command to the ; mov d_a_command,fdc_spec_cmd ;FDC (Intel 8272 or NEC 765) ; mov d_a_drive,0d0h ;step rate=3ms, head unload=0ms ; mov d_a_cylinder,20h ;head load=2ms, DMA mode true ; call fdc_command_put ;head unload and head load times ;are meaningless on mini floppies ;where the head is loaded ;when the motor is turned on mov ax,ds add data_buf_seg,ax ;fix up data buffer segment push es ! xor ax,ax mov es,ax mov di,disk_interrupt*4 ;point at vector location mov ax,offset i_disk stosw ;save and inc DI mov ax,cs stosw pop es set_up_video: mov dx,bw_card ;get the video chip port mov si,offset bw_table ;initialization commands mov cx,length bw_table ;how many commands call video_init ;send commands to port ;color board is similar ... push es mov di,bw_video_seg ;clear the video RAM mov es,di xor di,di mov cx,screen_siz + columns_per_screen ;screen length in words plus ;status line mov ax,0720h ;7 = default attribute ;20 = ASCII space rep stosw pop es sti retf ;initializaiton done ;---------- video_init: ;---------- xor bl,bl video_init_l: mov al,bl ! inc bl out dx,al ! inc dx lodsb ! out dx,al dec dx ! loop video_init_l add dx,4 mov al,video_on out dx,al ret last_code_offset equ offset $ DSEG ORG last_code_offset ; Loader BIOS data bw_table db 61h,50h,52h,0fh,19h,06h,19h,19h,02h,0dh,0bh,0ch,0,0,0,0 function_table: dw io_ret ; 0 console status dw io_ret ; 1 console input dw io_conout ; 2 console output dw io_ret ; 3 list status dw io_ret ; 4 list output dw io_ret ; 5 auxillary input dw io_ret ; 6 auxillary out dw io_ret ; 7 switch screen dw io_ret ; 8 update or print new status dw io_seldsk ; 9 select disk dw io_read ;10 read logical sector dw io_ret ;11 write logical sector dw io_ret ;12 flush buffers dw io_ret ;13 poll device ;short screen structure ;for IO_CONOUT ss0 dw 0,0,0 ;cursor, escape, screen_seg db 0,0 ;row,column ; Floppy Disk Driver data max_retries equ 3 retries db 0 ;retry counter max_recals equ 5 recals db 0 ; The following 2 tables are used to issue commands to, ; and read results from the 8272 FDC. The first entry ; in each table is the number of bytes to send or receive ; from the device disk_arguments db 0 ;number of arguments to send d_a_command db 0 ;command read/write d_a_drive db 0 ;drive select & head select d_a_cylinder db 0 ;cylinder to read/write d_a_head db 0 ;head d_a_record db 0 ;sector d_a_number db 2 ;magic number for 512 bytes/sector d_a_eot db sectors_per_track ;end of track sector number d_a_gpl db 02ah ;inter sector gap length d_a_dtl db 0ffh ;data length disk_results db 0 ;number of bytes to read d_r_st0 db 0 ;status byte 0 d_r_st1 db 0 ;status byte 1 d_r_st2 db 0 ;status byte 2 d_r_cylinder db 0 ;cylinder we are on now d_r_head db 0 d_r_record db 0 d_r_number db 0 ;number of sectors read f_a_bytes db 0 f_a_sectors db 0 f_a_gap db 0 f_a_filler db 0 fdc_rw_cmd db fdc_read_cmd ;read or write command dma_mode_storage db dma_mode_read_fdc num_sec db 1 ;num sectors to read/write ;in one call to controller track_mcnt rb 1 ;multi sector count on ;current track dma_low16 rw 1 ;20 bit address storage dma_high4 rb 1 motor_flags db 0 ;last state of the motor bits ; Disk Parameter Header dph0 dw 0 ;translate table dw 0,0,0 ;scratch area dw dpb0 ;dsk parm block dw 0 ;check dw 0 ;alloc vectors dw dir_bcb_hdr ;dir buff cntrl blk dw data_bcb_hdr ;data buff cntrl blk dw 0 ;hash table segment ; Disk Parameter Block dpb0 dw 8 ;sectors per track db 3 ;block shift db 7 ;block mask db 0 ;extnt mask dw 155 ;disk size in 1k blocks ;less offset track(s) dw 63 ;directory max db 11000000b ;alloc0 db 0 ;alloc1 dw 16 ;check size dw 1 ;offset db 2 ;phys sec shift db 3 ;phys sec mask dir_bcb_hdr dw dir_bcb db 1 dir_bcb db 0ffh ;drive db 0,0,0 ;record db 0,0 ;wflg,seg dw 0,0 ;track,sector dir_buf_off dw dir_buf ;buffer offset dw 0,0 ;link,pdadr data_bcb_hdr dw data_bcb db 1 data_bcb db 0ffh ;drive db 0,0,0 ;record db 0,0 ;wflg,seg dw 0,0 ;track,sector data_buf_seg dw data_buf;buffer segment, fixed up by LBIOS init: dw 0,0 ;link,pdadr ; Loader Program data signon db 'CCP/M-86 Loader 2.0$' nofile db cr,lf,'No CCPM.SYS File On Disk$' rerr db cr,lf,'Error Reading CCPM.SYS$' csegment db cr,lf,'Code Paragraph = $' dsegment db cr,lf,'Data Paragraph = $' crlf db cr,lf,'$' ccpmfcb db 1,'CCPM ','SYS',0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0 ; Note the Loader Program and Loader BIOS uninitialized ; data areas are combined to save space. ; Unitialized data for Loader Program ;read first CMD header of ;CCPM.SYS file here ctype rb 1 ;type clen rw 1 ;length cldseg rw 1 ;abs cmin rw 1 ;minimum cmax rw 1 ;maximum dtype rb 1 dlen rw 1 dldseg rw 1 dmin rw 1 dmax rw 1 org offset ctype sec1 rb 128 ;reserve space for the sector ; Unitialized data for Loader BIOS dir_buf equ ((offset $) + 15) and 0fff0h ; Sector for reads that span 64K boundaries; ; this sector can not cross a 64K boundary local_buf equ dir_buf + 512 data_buf equ (local_buf + 512)/16