; ======================================================= ; Ocean-240.2 ; CP/M BDOS at 0xC800:D5FF ; ; Disassembled by Romych 2025-09-09 ; ====================================================== INCLUDE "io.inc" INCLUDE "equates.inc" INCLUDE "ram.inc" IFNDEF BUILD_ROM OUTPUT bdos.bin ENDIF MODULE BDOS ORG 0xC800 bdos_start: LD SP, HL LD D, 0x00 NOP NOP LD L, E ; -------------------------------------------------- ; BDOS Entry point ; -------------------------------------------------- bdos_enter: JP bdos_entrance bdos_pere_addr: DW bdos_persub ; permanent error bdos_sele_addr: DW bdos_selsub ; select error bdos_rode_addr: DW bdos_rodsub ; write to ro disk error bdos_rofe_addr: DW bdos_rofsub ; write to ro file error ; ------------------------------------------------------- ; BDOS Handler ; Inp: C - func no ; DE or E - parameter ; Out: A or HL - result ; ------------------------------------------------------- bdos_entrance: ; store parameter DE EX DE, HL LD (CPM_VARS.bdos_info), HL EX DE, HL ; Store E LD A, E LD (CPM_VARS.bdos_linfo), A ; value to return, default = 0 LD HL, 0x00 LD (CPM_VARS.bdos_aret), HL ; Save user's stack pointer, set to local stack ADD HL, SP LD (CPM_VARS.bdos_usersp), HL LD SP, CPM_VARS.bdos_stack ; local stack setup XOR A LD (CPM_VARS.bdos_fcbdsk), A ; fcbdsk,resel=false LD (CPM_VARS.bdos_resel), A ; bdos_resel = FALSE LD HL, bdos_goback PUSH HL ; push goback address to return after jump LD A, C CP BDOS_NFUNCS RET NC ; return in func no out of range LD C, E ; store param E to C ; calculate offset in functab LD HL, functab ; DE=func_no, HL->functab LD E, A LD D, 0 ADD HL, DE ADD HL, DE LD E, (HL) INC HL LD D, (HL) ; DE=functab(func) LD HL, (CPM_VARS.bdos_info) ; restore input parameter EX DE, HL JP (HL) ; dispatch function ; ------------------------------------------------------- ; BDOS function handlers address table ; ------------------------------------------------------- functab: DW BIOS.wboot_f ; fn0 DW bdos_con_inp ; fn1 DW bdos_outcon ; fn2 DW bdos_aux_inp ; fn3 DW BIOS.punch_f ; fn4 DW BIOS.list_f ; fn5 DW bdos_dir_con_io ; fn6 DW bdos_get_io_byte ; fn7 DW bdos_set_io_byte ; fn8 DW bdos_print_str ; fn9 DW bdos_read ; fn10 0x0a DW bdos_get_con_st ; fn11 0x0b DW bdos_get_version ; fn12 0x0c DW bdos_reset_disks ; fn13 0x0d DW bdos_select_disk ; fn14 0x0e DW bdos_open_file ; fn15 0x0f DW bdos_close_file ; fn16 0x10 DW bdos_search_first ; fn17 0x11 DW bdos_search_next ; fn18 0x12 DW bdos_rm_file ; fn19 0x13 DW bdos_read_file ; fn20 0x14 DW bdos_write_file ; fn21 0x15 DW bdos_make_file ; fn22 0x16 DW bdos_ren_file ; fn23 0x17 DW bdos_get_login_vec ; fn24 0x18 DW bdos_get_cur_drive ; fn25 0x19 DW bdos_set_dma_addr ; fn26 0x1a DW bdos_get_alloc_addr ; fn27 0x1b DW bdos_set_ro ; fn28 0x1c DW bdos_get_wr_protect ; fn29 0x1d DW bdos_set_attr ; fn30 0x1e DW bdos_get_dpb ; fn31 0x1f DW bdos_set_user ; fn32 0x20 DW bdos_rand_read ; fn33 0x21 DW bdos_rand_write ; fn34 0x22 DW bdos_compute_fs ; fn35 0x23 DW bdos_set_random ; fn36 0x24 DW bdos_reset_drives ; fn37 0x25 DW bdos_not_impl ; fn38 0x26 (Access Drive) DW bdos_not_impl ; fn39 0x27 (Free Drive) DW bdos_rand_write_z ; fn40 0x28 ; ------------------------------------------------------- ; Report permanent error ; ------------------------------------------------------- bdos_persub: LD HL, permsg ; = 'B' CALL bdos_print_err CP CTRL_C JP Z, warm_boot RET ; ------------------------------------------------------- ; Report select error ; ------------------------------------------------------- bdos_selsub: LD HL, selmsg ;= 'S' JP bdos_wait_err ; ------------------------------------------------------- ; Report write to read/only disk ; ------------------------------------------------------- bdos_rodsub: LD HL, rodmsg ;= 'R' JP bdos_wait_err ; ------------------------------------------------------- ; Report read/only file ; ------------------------------------------------------- bdos_rofsub: LD HL, rofmsg ;= 'F' ; ------------------------------------------------------- ; Wait for response before boot ; ------------------------------------------------------- bdos_wait_err: CALL bdos_print_err JP warm_boot ; ------------------------------------------------------- ; Error messages ; ------------------------------------------------------- dskmsg: DB 'Bdos Err On ' dskerr: DB " : $" permsg: DB "Bad Sector$" selmsg: DB "Select$" rofmsg: DB "File " rodmsg: DB "R/O$" ; ------------------------------------------------------- ; Print error to console, message address in HL ; ------------------------------------------------------- bdos_print_err: PUSH HL ; save second message pointer CALL bdos_crlf ; set drive letter to message LD A, (CPM_VARS.bdos_curdsk) ADD A, 'A' LD (dskerr), A LD BC, dskmsg ; print first error message CALL bdos_print POP BC CALL bdos_print ; print second message ; ------------------------------------------------------- ; Console handlers ; Read console character to A ; ------------------------------------------------------- bdos_conin: LD HL, CPM_VARS.bdos_kbchar LD A, (HL) LD (HL), 0x00 OR A RET NZ ;no previous keyboard character ready JP BIOS.conin_f ; ------------------------------------------------------- ; Read character from console with echo ; ------------------------------------------------------- bdos_conech: CALL bdos_conin CALL bdos_chk_ctrl_char RET C PUSH AF LD C, A CALL bdos_outcon POP AF RET ; ------------------------------------------------------- ; Check for control char ; Onp: A - character ; Out: ZF if cr, lf, tab, or backspace ; CF if code < 0x20 ; ------------------------------------------------------- bdos_chk_ctrl_char: CP ASCII_CR RET Z CP ASCII_LF RET Z CP ASCII_TAB RET Z CP ASCII_BS RET Z CP ' ' RET ; ------------------------------------------------------- ; check console during output for ; Ctrl-S and Ctrl-C ; ------------------------------------------------------- bdos_conbrk: LD A, (CPM_VARS.bdos_kbchar) OR A JP NZ, .if_key ; skip if key pressed CALL BIOS.const_f AND 0x1 RET Z ; return if no char ready CALL BIOS.conin_f CP CTRL_S JP NZ, .no_crtl_s ; stop on crtl-s CALL BIOS.conin_f CP CTRL_C ; reboot on crtl-c JP Z, warm_boot XOR A RET .no_crtl_s: LD (CPM_VARS.bdos_kbchar), A ; character in A, save it .if_key: LD A, 0x1 ; return with true set in accumulator RET ; -------------------------------------------------- ; Out char C to screen (and printer) ; -------------------------------------------------- bdos_conout: LD A, (CPM_VARS.bdos_no_outflag) ; if set, skip output, only move cursor OR A JP NZ, .compout ; check console break PUSH BC CALL bdos_conbrk POP BC ; output c PUSH BC CALL BIOS.conout_f POP BC ; send to printer (list) device PUSH BC LD A, (CPM_VARS.bdos_prnflag) OR A CALL NZ, BIOS.list_f POP BC ; ------------------------------------------------------- ; Update cursor position ; ------------------------------------------------------- .compout: LD A, C ; recall the character ; and compute column position LD HL, CPM_VARS.bdos_column CP ASCII_DEL ; [DEL] key RET Z ; 0x7F dont move cursor INC (HL) ; inc column CP ASCII_SP ; return for normal character > 0x20 RET NC ; restore column position DEC (HL) LD A, (HL) OR A RET Z ; return if column=0 LD A, C CP ASCII_BS JP NZ, .not_backsp ; backspace character DEC (HL) ; BKSP -> column-1 RET .not_backsp: CP ASCII_LF RET NZ LD (HL), 0 ; LF -> column=0 RET ; ------------------------------------------------------- ; Send C character with possible preceding '^' char ; ------------------------------------------------------- bdos_ctlout: LD A, C CALL bdos_chk_ctrl_char ; cy if not graphic (or special case) JP NC, bdos_outcon ; normal output if non control char (tab, cr, lf) PUSH AF LD C, CTRL ; '^' CALL bdos_conout POP AF OR 0x40 ; convert to letter equivalent (^M, ^J and so on) LD C, A ; bdos_outcon: LD A, C CP ASCII_TAB JP NZ, bdos_conout ; Print spaces until tab stop position .to_tabpos: LD C, ' ' CALL bdos_conout LD A, (CPM_VARS.bdos_column) AND 00000111b ; column mod 8 = 0 ? JP NZ, .to_tabpos RET ; ------------------------------------------------------- ; Output backspace - erase previous character ; ------------------------------------------------------- bdos_out_bksp: CALL bdos_prn_bksp LD C, ' ' CALL BIOS.conout_f ; ------------------------------------------------------- ; Send backspace to console ; ------------------------------------------------------- bdos_prn_bksp: LD C, ASCII_BS JP BIOS.conout_f ; ------------------------------------------------------- ; print #, cr, lf for ctlx, ctlu, ctlr functions ; then move to strtcol (starting column) ; ------------------------------------------------------- bdos_newline: LD C, '#' CALL bdos_conout CALL bdos_crlf ; move the cursor to the starting position .print_sp: LD A, (CPM_VARS.bdos_column) ; a=column LD HL, CPM_VARS.bdos_strtcol CP (HL) RET NC ; ret id startcol>column LD C, ' ' ; print space CALL bdos_conout JP .print_sp ; ------------------------------------------------------- ; Out carriage return line feed sequence ; ------------------------------------------------------- bdos_crlf: LD C, ASCII_CR CALL bdos_conout LD C, ASCII_LF JP bdos_conout ; ------------------------------------------------------- ; Print $-ended string ; Inp: BC -> str$ ; ------------------------------------------------------- bdos_print: LD A, (BC) CP '$' RET Z INC BC PUSH BC LD C, A CALL bdos_outcon POP BC JP bdos_print ; ------------------------------------------------------- ; Buffered console input ; Reads characters from the keyboard into a memory buffer ; until RETURN is pressed. ; Inp: C=0Ah ; DE=address or zero ; ------------------------------------------------------- bdos_read: LD A, (CPM_VARS.bdos_column) LD (CPM_VARS.bdos_strtcol), A LD HL, (CPM_VARS.bdos_info) LD C, (HL) INC HL PUSH HL LD B, 0 ; B = current buffer length, ; C = maximum buffer length, ; HL= next to fill - 1 .readnx: PUSH BC PUSH HL .readn1: CALL bdos_conin AND 0x7f ; strip 7th bit POP HL POP BC ; end of line? CP ASCII_CR JP Z, .read_end CP ASCII_LF JP Z, .read_end ; backspace ? CP ASCII_BS JP NZ, .no_bksp LD A, B OR A JP Z, .readnx ; ignore BKSP for column=0 DEC B LD A, (CPM_VARS.bdos_column) LD (CPM_VARS.bdos_no_outflag), A JP .linelen ; not a backspace .no_bksp: CP ASCII_DEL ; [DEL] Key JP NZ, .no_del LD A, B OR A JP Z, .readnx ; ignore DEL for column=0 LD A, (HL) ; cur char DEC B ; dec column DEC HL ; point to previous char JP .echo ; out previous char .no_del: CP CTRL_E ; ^E - physical end of line JP NZ, .no_ctrle PUSH BC PUSH HL CALL bdos_crlf ; out CR+LF XOR A LD (CPM_VARS.bdos_strtcol), A ; reset start column to 0 JP .readn1 .no_ctrle: CP CTRL_P ; ^P JP NZ, .no_ctrlp PUSH HL LD HL, CPM_VARS.bdos_prnflag LD A, 0x1 SUB (HL) ; flip printer output flag LD (HL), A POP HL JP .readnx .no_ctrlp: CP CTRL_X ; cancel current cmd line JP NZ, .no_ctrlx POP HL .backx: LD A, (CPM_VARS.bdos_strtcol) LD HL, CPM_VARS.bdos_column CP (HL) JP NC, bdos_read DEC (HL) CALL bdos_out_bksp JP .backx .no_ctrlx: CP CTRL_U ; cancel current cmd line JP NZ, .no_ctrlu CALL bdos_newline POP HL JP bdos_read .no_ctrlu: CP CTRL_R ; repeats current cmd line JP NZ, .stor_echo ; start new line and retype .linelen: PUSH BC CALL bdos_newline POP BC POP HL PUSH HL PUSH BC .next_char: LD A, B OR A JP Z, .endof_cmd ; end of cmd line? INC HL LD C, (HL) DEC B PUSH BC PUSH HL CALL bdos_ctlout POP HL POP BC JP .next_char .endof_cmd: PUSH HL LD A, (CPM_VARS.bdos_no_outflag) OR A JP Z, .readn1 ; if no display, read next characters ; update cursor pos LD HL, CPM_VARS.bdos_column SUB (HL) LD (CPM_VARS.bdos_no_outflag), A ; move back compcol-column spaces .backsp: CALL bdos_out_bksp LD HL, CPM_VARS.bdos_no_outflag DEC (HL) JP NZ, .backsp JP .readn1 ; Put and Echo normal character .stor_echo: INC HL LD (HL), A INC B .echo: PUSH BC PUSH HL LD C, A CALL bdos_ctlout POP HL POP BC ; warm boot on ctrl+c only at start of line LD A, (HL) CP CTRL_C LD A, B JP NZ, .no_ctrlc CP 1 JP Z, warm_boot .no_ctrlc: CP C ; buffer is full? JP C, .readnx ; get next char in not full ; End of read operation, store length .read_end: POP HL LD (HL), B LD C, ASCII_CR JP bdos_conout ; Our CR and return ; ------------------------------------------------------- ; Console input with echo (C_READ) ; Out: A=L=character ; ------------------------------------------------------- bdos_con_inp: CALL bdos_conech JP bdos_ret_a ; ------------------------------------------------------- ; Console input chartacter ; Out: A=L=ASCII character ; ------------------------------------------------------- bdos_aux_inp: CALL BIOS.reader_f JP bdos_ret_a ; ------------------------------------------------------- ; Direct console I/O ; Inp: E=code. ; E=code. Returned values (in A) vary. ; 0xff Return a character without echoing if one is ; waiting; zero if none ; 0xfe Return console input status. Zero if no ; character is waiting ; Out: A ; ------------------------------------------------------- bdos_dir_con_io: LD A, C INC A JP Z, .dirinp INC A JP Z, BIOS.const_f JP BIOS.conout_f .dirinp: CALL BIOS.const_f OR A JP Z, bdos_ret_mon CALL BIOS.conin_f JP bdos_ret_a ; ------------------------------------------------------- ; Get I/O byte ; Out: A = I/O byte. ; ------------------------------------------------------- bdos_get_io_byte: LD A, (iobyte) JP bdos_ret_a ; ------------------------------------------------------- ; Set I/O byte ; Inp: C=I/O byte. ; TODO: Check, will be E ? ; ------------------------------------------------------- bdos_set_io_byte: LD HL, iobyte LD (HL), C RET ; ------------------------------------------------------- ; Out $-ended string ; Inp: DE -> string$. ; ------------------------------------------------------- bdos_print_str: EX DE, HL LD C, L LD B, H JP bdos_print ; ------------------------------------------------------- ; Get console status ; Inp: A=L=status ; ------------------------------------------------------- bdos_get_con_st: CALL bdos_conbrk bdos_ret_a: LD (CPM_VARS.bdos_aret), A bdos_not_impl: RET ; ------------------------------------------------------- ; Set IO Error status = 1 ; ------------------------------------------------------- set_ioerr_1: LD A, 1 JP bdos_ret_a ; ------------------------------------------------------- ; Report select error ; ------------------------------------------------------- bdos_sel_error: LD HL, bdos_sele_addr ; indirect Jump to [HL] bdos_jump_hl: LD E, (HL) INC HL LD D, (HL) ; ->bdos_sele_addr+1 EX DE, HL JP (HL) ; ->bdos_selsub ; ------------------------------------------------------- ; Move C bytes from [DE] to [HL] ; ------------------------------------------------------- bdos_move_dehl: INC C .move_next: DEC C RET Z LD A, (DE) LD (HL), A INC DE INC HL JP .move_next ; ------------------------------------------------------- ; Select the disk drive given by curdsk, and fill ; the base addresses curtrka - alloca, then fill ; the values of the disk parameter block ; Out: ZF - for big disk ; ------------------------------------------------------- bdos_sel_dsk: LD A, (CPM_VARS.bdos_curdsk) LD C, A CALL BIOS.seldsk_f ; HL filled by call ; HL = 0000 if error, otherwise -> DPH LD A, H OR L RET Z ; return if not valid drive LD E, (HL) INC HL LD D, (HL) ; DE -> disk header ; save pointers INC HL LD (CPM_VARS.bdos_cdrmax), HL INC HL INC HL LD (CPM_VARS.bdos_curtrk), HL INC HL INC HL LD (CPM_VARS.bdos_currec), HL INC HL INC HL ; save translation vector (table) addr EX DE, HL LD (CPM_VARS.bdos_tranv), HL ; tran vector LD HL, CPM_VARS.bdos_buffa ; DE=source for move, HL=dest LD C, 8 CALL bdos_move_dehl ; Fill the disk parameter block LD HL, (CPM_VARS.bdos_dpbaddr) EX DE, HL LD HL, CPM_VARS.bdos_sectpt LD C, 15 CALL bdos_move_dehl ; Set single/double map mode LD HL, (CPM_VARS.bdos_maxall) ; largest allocation number LD A, H ; a=h>0 for large disks (>255 blocks) LD HL, CPM_VARS.bdos_small_disk LD (HL), TRUE ; small OR A JP Z, .is_small LD (HL), FALSE ; big disk > 255 sec .is_small: LD A, 0xff OR A RET ; ------------------------------------------------------- ; Move to home position, ; then reset pointers for track and sector ; ------------------------------------------------------- bdos_home: CALL BIOS.home_f ; home head XOR A ; set track pointer to 0x0000 LD HL, (CPM_VARS.bdos_curtrk) LD (HL), A INC HL LD (HL), A ; set current record pointer to 0x0000 LD HL, (CPM_VARS.bdos_currec) LD (HL), A INC HL LD (HL), A RET ; ------------------------------------------------------- ; Read buffer and check condition ; ------------------------------------------------------- bdos_rdbuff: CALL BIOS.read_f JP diocomp ; check for i/o errors ; ------------------------------------------------------- ; Write buffer and check condition ; Inp: C - write type ; 0 - normal write operation ; 1 - directory write operation ; 2 - start of new block ; ------------------------------------------------------- bdos_wrbuff: CALL BIOS.write_f ; ------------------------------------------------------- ; Check for disk errors ; ------------------------------------------------------- diocomp: OR A RET Z LD HL, bdos_pere_addr ; = C899h JP bdos_jump_hl ; ------------------------------------------------------- ; Seek the record containing the current dir entry ; ------------------------------------------------------- bdos_seekdir: LD HL, (CPM_VARS.bdos_dcnt) LD C, DSK_SHF CALL bdos_hlrotr LD (CPM_VARS.bdos_arecord), HL LD (CPM_VARS.bdos_drec), HL ; ------------------------------------------------------- ; Seek the track given by arecord (actual record) ; local equates for registers ; ------------------------------------------------------- bdos_seek: LD HL, CPM_VARS.bdos_arecord LD C, (HL) INC HL LD B, (HL) ; BC = sector number LD HL, (CPM_VARS.bdos_currec) LD E, (HL) INC HL LD D, (HL) ; DE = current sector number LD HL, (CPM_VARS.bdos_curtrk) LD A, (HL) INC HL LD H, (HL) LD L, A ; HL = current track .sec_less_cur: ; check specified sector before current LD A, C SUB E LD A, B SBC A, D JP NC, .sec_ge_cur ; yes, decrement current sectors for one track PUSH HL LD HL, (CPM_VARS.bdos_sectpt) ; sectors per track LD A, E SUB L LD E, A LD A, D SBC A, H LD D, A ; current track-1 POP HL DEC HL JP .sec_less_cur ; specified sector >= current .sec_ge_cur: PUSH HL LD HL, (CPM_VARS.bdos_sectpt) ADD HL, DE ; current sector+track JP C, .track_found ; jump if after current ; sector before current LD A, C SUB L LD A, B SBC A, H JP C, .track_found EX DE, HL POP HL ; increment track INC HL JP .sec_ge_cur ; determined track number with specified sector .track_found: POP HL PUSH BC PUSH DE PUSH HL ; Stack contains (lowest) BC=arecord, DE=currec, HL=curtrk EX DE, HL LD HL, (CPM_VARS.bdos_offset) ; adjust if have reserved tracks ADD HL, DE LD B, H LD C, L CALL BIOS.settrk_f ; select track ; save current track POP DE ; curtrk LD HL, (CPM_VARS.bdos_curtrk) LD (HL), E INC HL LD (HL), D ; save current record POP DE LD HL, (CPM_VARS.bdos_currec) LD (HL), E INC HL LD (HL), D ; calc relative offset between arecord and currec POP BC LD A, C SUB E LD C, A LD A, B SBC A, D LD B, A LD HL, (CPM_VARS.bdos_tranv) ; HL -> translation table EX DE, HL ; translate via BIOS call CALL BIOS.sectran_f ; select translated sector and return LD C, L LD B, H JP BIOS.setsec_f ; ------------------------------------------------------- ; Compute block number from record number and extent ; number ; ------------------------------------------------------- bdos_dm_position: LD HL, CPM_VARS.bdos_blkshf LD C, (HL) LD A, (CPM_VARS.bdos_vrecord) ; record number ; a = a / 2^blkshf dmpos_l0: OR A RRA DEC C JP NZ, dmpos_l0 ; A = shr(vrecord, blkshf) = vrecord/2^(sect/block) LD B, A LD A, 8 SUB (HL) ; c = 8 - blkshf LD C, A LD A, (CPM_VARS.bdos_extval) ; a = extval * 2^(8-blkshf) dmpos_l1: DEC C JP Z, dmpos_l2 OR A RLA JP dmpos_l1 dmpos_l2: ADD A, B RET ; with dm_position in A ; ------------------------------------------------------- ; Return disk map value from position given by BC ; ------------------------------------------------------- bdos_getdm: LD HL, (CPM_VARS.bdos_info) LD DE, DSK_MAP ADD HL, DE ADD HL, BC LD A, (CPM_VARS.bdos_small_disk) OR A JP Z,getdmd LD L, (HL) LD H, 0x00 RET getdmd: ADD HL, BC LD E, (HL) INC HL LD D, (HL) EX DE, HL RET ; ------------------------------------------------------- ; Compute disk block number from current FCB ; ------------------------------------------------------- bdos_index: CALL bdos_dm_position LD C, A LD B, 0 CALL bdos_getdm LD (CPM_VARS.bdos_arecord), HL RET ; ------------------------------------------------------- ; Called following index to see if block allocated ; Out: ZF if not allocated ; ------------------------------------------------------- bdos_allocated: LD HL, (CPM_VARS.bdos_arecord) LD A, L OR H RET ; ------------------------------------------------------- ; Compute actual record address, assuming index called ; ------------------------------------------------------- bdos_atran: LD A, (CPM_VARS.bdos_blkshf) LD HL, (CPM_VARS.bdos_arecord) bdatarn_l0: ADD HL, HL DEC A JP NZ, bdatarn_l0 LD (CPM_VARS.bdos_arecord1), HL LD A, (CPM_VARS.bdos_blmsk) LD C, A LD A, (CPM_VARS.bdos_vrecord) AND C OR L LD L, A LD (CPM_VARS.bdos_arecord), HL RET ; ------------------------------------------------------- ; Get current extent field address to A ; ------------------------------------------------------- bdos_getexta: LD HL, (CPM_VARS.bdos_info) LD DE, FCB_EXT ADD HL, DE ; HL -> .fcb(extnum) RET ; ------------------------------------------------------- ; Compute reccnt and nxtrec addresses for get/setfcb ; Out: HL -> record count byte in fcb ; DE -> next record number byte in fcb ; ------------------------------------------------------- bdos_fcb_rcnr: LD HL, (CPM_VARS.bdos_info) LD DE, FCB_RC ADD HL, DE EX DE, HL LD HL, 17 ; (nxtrec-reccnt) ADD HL, DE RET ; ------------------------------------------------------- ; Set variables from currently addressed fcb ; ------------------------------------------------------- bdos_stor_fcb_var: CALL bdos_fcb_rcnr LD A, (HL) LD (CPM_VARS.bdos_vrecord), A EX DE, HL LD A, (HL) LD (CPM_VARS.bdos_rcount), A CALL bdos_getexta LD A, (CPM_VARS.bdos_extmsk) AND (HL) LD (CPM_VARS.bdos_extval), A RET ; ------------------------------------------------------- ; Set the next record to access. ; Inp: HL -> fcb next record byte ; ------------------------------------------------------- bdos_set_nxt_rec: CALL bdos_fcb_rcnr LD A, (CPM_VARS.bdos_seqio) ; sequential mode flag (=1) CP 2 JP NZ, bsfcb_l0 XOR A ; if 2 - no adder needed bsfcb_l0: LD C, A LD A, (CPM_VARS.bdos_vrecord) ; last record number ADD A, C LD (HL), A EX DE, HL LD A, (CPM_VARS.bdos_rcount) ; next record byte LD (HL), A RET ; ------------------------------------------------------- ; Rotate HL right by C bits ; ------------------------------------------------------- bdos_hlrotr: INC C bhlr_nxt: DEC C RET Z LD A, H OR A RRA LD H, A LD A,L RRA LD L, A JP bhlr_nxt ; ------------------------------------------------------- ; Compute checksum for current directory buffer ; Out: A - checksum ; ------------------------------------------------------- bdos_compute_cs: LD C, DIR_BUFF_SIZE ; size of directory buffer LD HL, (CPM_VARS.bdos_buffa) XOR A bcompcs_add_nxt: ADD A, (HL) INC HL DEC C JP NZ, bcompcs_add_nxt RET ; ------------------------------------------------------- ; Rotate HL left by C bits ; ------------------------------------------------------- bdos_hlrotl: INC C .nxt: DEC C RET Z ADD HL, HL JP .nxt ; ------------------------------------------------------- ; Set a "1" value in curdsk position ; Inp: BC - vector ; Out: HL - vector with bit set ; ------------------------------------------------------- bdos_set_dsk_bit: PUSH BC LD A, (CPM_VARS.bdos_curdsk) LD C, A LD HL, 0x0001 CALL bdos_hlrotl POP BC LD A, C OR L LD L, A LD A, B OR H LD H, A RET ; ------------------------------------------------------- ; Get write protect status ; ------------------------------------------------------- bdos_nowrite: LD HL, (CPM_VARS.bdos_rodsk) LD A, (CPM_VARS.bdos_curdsk) LD C, A CALL bdos_hlrotr LD A, L AND 0x1 RET ; ------------------------------------------------------- ; Temporarily set current drive to be read-only; ; attempts to write to it will fail ; ------------------------------------------------------- bdos_set_ro: LD HL, CPM_VARS.bdos_rodsk LD C, (HL) INC HL LD B, (HL) CALL bdos_set_dsk_bit LD (CPM_VARS.bdos_rodsk), HL ; high water mark in directory goes to max LD HL, (CPM_VARS.bdos_dirmax) INC HL EX DE, HL LD HL, (CPM_VARS.bdos_cdrmax) LD (HL), E INC HL LD (HL), D RET ; ------------------------------------------------------- ; Check current directory element for read/only status ; ------------------------------------------------------- bdos_check_rodir: CALL bdos_getdptra ; ------------------------------------------------------- ; Check current buff(dptr) or fcb(0) for r/o status ; ------------------------------------------------------- bdos_check_rofile: LD DE, RO_FILE ADD HL, DE LD A, (HL) RLA RET NC LD HL, bdos_rofe_addr ; = C8B1h JP bdos_jump_hl ; ------------------------------------------------------- ; Check for write protected disk ; ------------------------------------------------------- bdos_check_write: CALL bdos_nowrite RET Z LD HL, bdos_rode_addr ; = C8ABh JP bdos_jump_hl ; ------------------------------------------------------- ; Compute the address of a directory element at ; positon dptr in the buffer ; ------------------------------------------------------- bdos_getdptra: LD HL, (CPM_VARS.bdos_buffa) LD A, (CPM_VARS.bdos_dptr) ; ------------------------------------------------------- ; HL = HL + A ; ------------------------------------------------------- bdos_hl_add_a: ADD A,L LD L, A RET NC INC H RET ; ------------------------------------------------------- ; Compute the address of the s2 from fcb ; Out: A - module number ; HL -> module number ; ------------------------------------------------------- bdos_get_s2: LD HL, (CPM_VARS.bdos_info) LD DE, FCB_S2 ADD HL, DE LD A, (HL) RET ; ------------------------------------------------------- ; Clear the module number field for user open/make ; ------------------------------------------------------- bdos_clear_s2: CALL bdos_get_s2 LD (HL), 0 RET ; ------------------------------------------------------- ; Set FWF (File Write Flag) ; ------------------------------------------------------- bdos_set_fwf: CALL bdos_get_s2 OR FWF_MASK LD (HL), A RET ; ------------------------------------------------------- ; Check for free entries in directory ; Out: CF is set - directory have more file entries ; CF is reset - directory is full ; ------------------------------------------------------- bdos_dir_is_free: LD HL, (CPM_VARS.bdos_dcnt) EX DE, HL ; DE = directory counter LD HL, (CPM_VARS.bdos_cdrmax) ; HL = cdrmax LD A, E SUB (HL) INC HL LD A, D SBC A, (HL) ; Condition dcnt - cdrmax produces cy if cdrmax>dcnt RET ; ------------------------------------------------------- ; If not (cdrmax > dcnt) then cdrmax = dcnt+1 ; ------------------------------------------------------- bdos_setcdr: CALL bdos_dir_is_free RET C INC DE LD (HL), D DEC HL LD (HL), E RET ; ------------------------------------------------------- ; HL = DE - HL ; ------------------------------------------------------- bdos_de_sub_hl: LD A, E SUB L LD L, A LD A, D SBC A, H LD H, A RET ; Set directory checksubm byte bdos_newchecksum: LD C, 0xff ; ------------------------------------------------------- bdos_checksum: LD HL, (CPM_VARS.bdos_drec) EX DE, HL LD HL, (CPM_VARS.bdos_alloc1) CALL bdos_de_sub_hl RET NC PUSH BC CALL bdos_compute_cs ; Compute checksum to A for current dir buffer LD HL, (CPM_VARS.bdos_cksum) EX DE, HL LD HL, (CPM_VARS.bdos_drec) ADD HL, DE POP BC INC C JP Z, initial_cs CP (HL) RET Z CALL bdos_dir_is_free RET NC CALL bdos_set_ro RET initial_cs: LD (HL), A RET ; ------------------------------------------------------- ; Write the current directory entry, set checksum ; ------------------------------------------------------- bdos_wrdir: CALL bdos_newchecksum CALL bdos_setdir LD C, 0x1 CALL bdos_wrbuff JP bdos_setdata ; ------------------------------------------------------- ; Read a directory entry into the directory buffer ; ------------------------------------------------------- bdos_rd_dir: CALL bdos_setdir CALL bdos_rdbuff ; ------------------------------------------------------- ; Set data dma address ; ------------------------------------------------------- bdos_setdata: LD HL, CPM_VARS.bdos_dmaad JP bdos_set_dma ; ------------------------------------------------------- ; Set directory dma address ; ------------------------------------------------------- bdos_setdir: LD HL, CPM_VARS.bdos_buffa ; ------------------------------------------------------- ; HL -> dma address to set (i.e., buffa or dmaad) ; ------------------------------------------------------- bdos_set_dma: LD C, (HL) INC HL LD B, (HL) JP BIOS.setdma_f ; ------------------------------------------------------- ; Copy the directory entry to the user buffer ; after call to search or searchn by user code ; ------------------------------------------------------- bdos_dir_to_user: LD HL, (CPM_VARS.bdos_buffa) EX DE, HL LD HL, (CPM_VARS.bdos_dmaad) LD C, DIR_BUFF_SIZE JP bdos_move_dehl ; ------------------------------------------------------- ; return zero flag if at end of directory, non zero ; if not at end (end of dir if dcnt = 0ffffh) ; ------------------------------------------------------- bdos_end_of_dir: LD HL, CPM_VARS.bdos_dcnt LD A, (HL) INC HL CP (HL) RET NZ INC A RET ; ------------------------------------------------------- ; Set dcnt to the end of the directory ; ------------------------------------------------------- bdos_set_end_dir: LD HL, ENDDIR LD (CPM_VARS.bdos_dcnt), HL RET ; ------------------------------------------------------- ; Read next directory entry, with C=true if initializing ; ------------------------------------------------------- bdos_read_dir: LD HL, (CPM_VARS.bdos_dirmax) EX DE, HL LD HL, (CPM_VARS.bdos_dcnt) INC HL LD (CPM_VARS.bdos_dcnt), HL CALL bdos_de_sub_hl JP NC, bdrd_l0 JP bdos_set_end_dir bdrd_l0: LD A, (CPM_VARS.bdos_dcnt) AND DSK_MSK LD B, FCB_SHF bdrd_l1: ADD A, A DEC B JP NZ, bdrd_l1 LD (CPM_VARS.bdos_dptr), A OR A RET NZ PUSH BC CALL bdos_seekdir CALL bdos_rd_dir POP BC JP bdos_checksum ; ------------------------------------------------------- ; Given allocation vector position BC, return with byte ; containing BC shifted so that the least significant ; bit is in the low order accumulator position. HL is ; the address of the byte for possible replacement in ; memory upon return, and D contains the number of shifts ; required to place the returned value back into position ; ------------------------------------------------------- bdos_getallocbit: LD A, C AND 00000111b INC A LD E, A LD D, A LD A, C RRCA RRCA RRCA AND 00011111b LD C, A LD A, B ADD A, A ADD A, A ADD A, A ADD A, A ADD A, A OR C LD C, A LD A, B RRCA RRCA RRCA AND 00011111b LD B, A LD HL, (CPM_VARS.bdos_alloca) ; Base address of allocation vector ADD HL, BC LD A, (HL) ga_rotl: RLCA DEC E JP NZ, ga_rotl RET ; ------------------------------------------------------- ; BC is the bit position of ALLOC to set or reset. The ; value of the bit is in register E. ; ------------------------------------------------------- bdos_setallocbit: PUSH DE CALL bdos_getallocbit AND 11111110b POP BC OR C ; ------------------------------------------------------- ; Rotate and replace ; Inp: A - Byte value from ALLOC ; D - bit position (1..8) ; HL - target ALLOC position ; ------------------------------------------------------- bdos_sab_rotr: RRCA DEC D JP NZ, bdos_sab_rotr LD (HL), A RET ; ------------------------------------------------------- ; Set or clear space used bits in allocation map ; Inp: C=0 - clear, C=1 - set ; ------------------------------------------------------- bdos_scandm: CALL bdos_getdptra ; HL addresses the beginning of the directory entry LD DE, DSK_MAP ADD HL, DE ; HL -> block number byte PUSH BC LD C, 17 ; fcblen-dskmap+1 ; scan all 17 bytes .scan_next: POP DE DEC C RET Z ; return if done PUSH DE ; small or bug disk? LD A, (CPM_VARS.bdos_small_disk) OR A JP Z, .scan_big PUSH BC PUSH HL ; small, one byte LD C, (HL) LD B, 0 JP .scan_cmn .scan_big: ; big, two byte DEC C PUSH BC LD C, (HL) INC HL LD B, (HL) PUSH HL .scan_cmn: ; this block used? LD A, C OR B JP Z, .not_used ; have free space on disk? LD HL, (CPM_VARS.bdos_maxall) LD A, L SUB C LD A, H SBC A, B CALL NC, bdos_setallocbit ; enough space, set bit .not_used: POP HL ; HL -> next block in fcb INC HL POP BC JP .scan_next ; ------------------------------------------------------- ; Initialize the current disk ; lret = false, set to true if $ file exists ; compute the length of the allocation vector - 2 ; ------------------------------------------------------- bdos_initialize: ; compute size of alloc table LD HL, (CPM_VARS.bdos_maxall) LD C, 3 CALL bdos_hlrotr ; HL = disk size / 8 + 1 INC HL LD B, H LD C, L ; BC = alloc table size LD HL, (CPM_VARS.bdos_alloca) ; address of allocation table ; fill with zeroes .alt_fill_0: LD (HL), 0 INC HL DEC BC LD A, B OR C JP NZ, .alt_fill_0 LD HL, (CPM_VARS.bdos_dirblk) ; DE = initial space, used by directory EX DE, HL LD HL, (CPM_VARS.bdos_alloca) ; HL -> allocation map LD (HL), E INC HL LD (HL), D ; [HL] = DE CALL bdos_home ; home drive, set initial head, track, sector LD HL, (CPM_VARS.bdos_cdrmax) LD (HL), 3 ; next dir read INC HL LD (HL), 0 CALL bdos_set_end_dir ; mark end of dir .rd_next_f: ; read next file name LD C, 0xff CALL bdos_read_dir CALL bdos_end_of_dir ; is have another file? RET Z ; get params of file, and check deleted CALL bdos_getdptra LD A, FILE_DELETED CP (HL) JP Z, .rd_next_f ; check user code LD A, (CPM_VARS.bdos_userno) CP (HL) JP NZ, .set_as_used INC HL LD A, (HL) SUB '$' ; check for $name file JP NZ, .set_as_used ; dollar file found, mark in lret DEC A LD (CPM_VARS.bdos_aret), A .set_as_used: LD C, 1 CALL bdos_scandm CALL bdos_setcdr JP .rd_next_f ; ------------------------------------------------------- ; Return directory location as lret ; used in delete, rename, ... ; ------------------------------------------------------- bdos_ret_dirloc: LD A, (CPM_VARS.bdos_dirloc) JP bdos_ret_a ; ------------------------------------------------------- ; Compare extent# in A with that in C, return nonzero ; if they do not match ; ------------------------------------------------------- bdos_compext: PUSH BC PUSH AF LD A, (CPM_VARS.bdos_extmsk) CPL LD B, A LD A, C AND B LD C, A POP AF AND B SUB C AND MAX_EXT ; check only bits [4:0] (MAX_EXT) POP BC RET ; ------------------------------------------------------- ; Search for directory element of length C at info ; ------------------------------------------------------- bdos_search: LD A, 0xff LD (CPM_VARS.bdos_dirloc), A LD HL, CPM_VARS.bdos_searchl ; length in bytes to match LD (HL), C LD HL, (CPM_VARS.bdos_info) ; address filename to match LD (CPM_VARS.bdos_searcha), HL CALL bdos_set_end_dir CALL bdos_home ; ------------------------------------------------------- ; Search for the next directory element, assuming ; a previous call on search which sets searcha and ; searchl ; ------------------------------------------------------- bdos_search_nxt: LD C, 0 CALL bdos_read_dir ; get next filename entry CALL bdos_end_of_dir ; at end? pos=0xffff JP Z, .not_found ; jump at end LD HL, (CPM_VARS.bdos_searcha) EX DE, HL ; skip if file deleted LD A, (DE) CP FILE_DELETED JP Z, .search_next PUSH DE CALL bdos_dir_is_free POP DE JP NC, .not_found ; Is full, no more file entries .search_next: CALL bdos_getdptra ; get address of FCB LD A, (CPM_VARS.bdos_searchl) ; BC=length to compare LD C, A LD B, 0 ; compare loop .search_loop: LD A, C OR A JP Z, .search_end ; search completed? ; check byte LD A, (DE) CP '?' JP Z, .search_ok ; match if wildcard LD A, B CP 13 ; ignore 13th byte JP Z, .search_ok CP FCB_EXT ; extent byte LD A, (DE) JP Z, .search_ext SUB (HL) ; compare bytes AND 0x7f ; mask 7th bit JP NZ, bdos_search_nxt ; if not match, check next file entry JP .search_ok ; compare ext .search_ext: PUSH BC LD C, (HL) CALL bdos_compext POP BC JP NZ, bdos_search_nxt ; if not match, check next file entry ; current bytes matches, increment pointers, decrement counter .search_ok: INC DE INC HL INC B DEC C JP .search_loop ; compare next byte ; entiry name matches, return dir position .search_end: LD A, (CPM_VARS.bdos_dcnt) AND 0x3 LD (CPM_VARS.bdos_aret), A LD HL, CPM_VARS.bdos_dirloc LD A, (HL) RLA RET NC XOR A LD (HL), A RET ; end of directory, or empty name .not_found: CALL bdos_set_end_dir LD A, 0xff JP bdos_ret_a ; ------------------------------------------------------- ; Delete the currently addressed file ; ------------------------------------------------------- bdos_era_file: CALL bdos_check_write ; check write rotection LD C, 12 ; length of filename CALL bdos_search ; search file in directory (with wildcards) .del_next: CALL bdos_end_of_dir RET Z ; no more dir entries - exit CALL bdos_check_rodir ; check file RO CALL bdos_getdptra ; get file info LD (HL), FILE_DELETED ; set deleted marker at first symbol LD C, 0 CALL bdos_scandm ; clear space at map CALL bdos_wrdir ; write directory to disk CALL bdos_search_nxt ; find next file JP .del_next ; ------------------------------------------------------- ; Given allocation vector position BC, find the zero bit ; closest to this position by searching left and right. ; if found, set the bit to one and return the bit position ; in hl. if not found (i.e., we pass 0 on the left, or ; maxall on the right), return 0000 in hl ; ------------------------------------------------------- bdos_get_block: LD D, B LD E, C .prev_test: LD A, C OR B JP Z, .next_test ; jump if block 0 specified ; check previous block DEC BC PUSH DE PUSH BC CALL bdos_getallocbit RRA JP NC, .ret_block ; block is empty, use it ; not empty, check more POP BC POP DE ; look at next block .next_test: ; check for free space on disk LD HL, (CPM_VARS.bdos_maxall) LD A, E SUB L LD A, D SBC A, H JP NC, .ret_no_block ; have space, move to next block INC DE PUSH BC PUSH DE LD B, D LD C, E CALL bdos_getallocbit RRA JP NC, .ret_block ; block is empty, use it POP DE POP BC JP .prev_test ; mark block as used and return in HL .ret_block: RLA INC A CALL bdos_sab_rotr POP HL POP DE RET ; no free blocks found .ret_no_block: LD A, C OR B JP NZ, .prev_test ; if BC != 0 try to find before LD HL, 0x00 ; not found RET ; ------------------------------------------------------- ; Copy the entire file control block ; ------------------------------------------------------- bdos_copy_fcb: LD C, 0x00 LD E, FCB_LEN ; ------------------------------------------------------- ; copy fcb information starting at C for E bytes ; into the currently addressed directory entry ; ------------------------------------------------------- dbos_copy_dir: PUSH DE LD B, 0x00 LD HL, (CPM_VARS.bdos_info) ADD HL, BC EX DE, HL CALL bdos_getdptra POP BC CALL bdos_move_dehl ; ------------------------------------------------------- ; Enter from close to seek and copy current element ; ------------------------------------------------------- bdos_seek_copy: CALL bdos_seekdir JP bdos_wrdir ; ------------------------------------------------------- ; Rename the file described by the first half of ; the currently addressed FCB to new name, contained in ; the last half of the currently addressed FCB. ; ------------------------------------------------------- bdos_rename: CALL bdos_check_write ; check for RO disk LD C, 12 ; use 12 symbols of name to compare CALL bdos_search ; find first file LD HL, (CPM_VARS.bdos_info) ; file info LD A, (HL) ; user number LD DE, 16 ; shift to place second file info ADD HL, DE LD (HL), A ; set same user no .ren_next: CALL bdos_end_of_dir RET Z ; return if end of directory CALL bdos_check_rodir ; check file RO flag LD C, 16 ; start from 16th byte LD E, FN_LEN ; and copy 12 byte CALL dbos_copy_dir CALL bdos_search_nxt ; search next file JP .ren_next ; ------------------------------------------------------- ; Update file attributes for current fcb ; ------------------------------------------------------- bdos_update_attrs: LD C, FN_LEN CALL bdos_search ; search file by 12 bytes of name .set_next: CALL bdos_end_of_dir RET Z ; return if not found LD C, 0 LD E, FN_LEN CALL dbos_copy_dir ; copy name to FCB and save dir CALL bdos_search_nxt JP .set_next ; ; do it for next file ; -------------------------------------------------- ; Open file, name specified in FCB ; ------------------------------------------------------- open: LD C, FCB_INFO_LEN CALL bdos_search ; search file CALL bdos_end_of_dir RET Z ; return if not found bdos_open_copy: CALL bdos_getexta LD A, (HL) ; get extent byte PUSH AF ; save ext PUSH HL ; and it's address CALL bdos_getdptra EX DE, HL ; move to user space LD HL, (CPM_VARS.bdos_info) LD C, 32 PUSH DE CALL bdos_move_dehl CALL bdos_set_fwf ; set 7th bit s2 "unmodified" flag POP DE LD HL, FCB_EXT ADD HL, DE LD C, (HL) ; C = extent byte LD HL, FCB_RC ADD HL, DE LD B, (HL) ; B = record count POP HL POP AF LD (HL), A LD A, C CP (HL) ; cmp extent bytes LD A, B JP Z, .set_rec_cnt ; user specified extent is not same, reset record count to 0 LD A, 0 JP C, .set_rec_cnt ; set to maximum LD A, 128 .set_rec_cnt: ; set record count in user FCB to A LD HL, (CPM_VARS.bdos_info) LD DE, FCB_RC ADD HL, DE LD (HL), A RET ; -------------------------------------------------- ; HL = .fcb1(i), DE = .fcb2(i), ; if fcb1(i) = 0 then fcb1(i) := fcb2(i) ; -------------------------------------------------- bdos_mergezero: LD A, (HL) INC HL OR (HL) DEC HL RET NZ LD A, (DE) LD (HL), A INC DE INC HL LD A, (DE) LD (HL), A DEC DE DEC HL RET ; -------------------------------------------------- ; Close file specified by FCB ; -------------------------------------------------- bdos_close: XOR A ; clear status and file position bytes LD (CPM_VARS.bdos_aret), A LD (CPM_VARS.bdos_dcnt), A LD (CPM_VARS.bdos_dcnt+1), A CALL bdos_nowrite ; get write protection RET NZ ; return if set CALL bdos_get_s2 AND FWF_MASK RET NZ ; return if not modified flag set ; search file LD C, FCB_INFO_LEN CALL bdos_search CALL bdos_end_of_dir RET Z ; return if not found LD BC, DSK_MAP ; offset of records used CALL bdos_getdptra ADD HL, BC EX DE, HL LD HL, (CPM_VARS.bdos_info) ; same for user FCB ADD HL, BC LD C, 16 ; bytes in extent bdos_merge0: LD A, (CPM_VARS.bdos_small_disk) ; small/big disk flag OR A JP Z, bdos_merge ; jump if big (16 bit) ; small disk (8 bit) LD A, (HL) ; from user FCB OR A LD A, (DE) ; from DIR FCB JP NZ, bdm_fcbnzero LD (HL), A ; user is 0, set from directory fcb bdm_fcbnzero: OR A JP NZ, bdm_buffnzero ; dir is 0, set from user fcb LD A, (HL) LD (DE), A bdm_buffnzero: CP (HL) JP NZ, bdm_mergerr ; if both non zero, close error JP bdm_dmset ; merged ok, go to next fcb byte bdos_merge: CALL bdos_mergezero ; update user fcb if it is zero EX DE, HL CALL bdos_mergezero ; update dir fcb if it is zero EX DE, HL LD A, (DE) CP (HL) JP NZ, bdm_mergerr ; if both is same, close error ; next byte INC DE INC HL LD A, (DE) CP (HL) JP NZ, bdm_mergerr ; if both is same, close error DEC C bdm_dmset: ; next INC DE INC HL DEC C JP NZ, bdos_merge0 ; merge next if C>0 LD BC, 0xffec ; -(fcblen-extnum) (-20) ADD HL, BC EX DE, HL ADD HL, BC LD A, (DE) CP (HL) JP C, bdm_endmerge ; directory extent > user extent -> end ; update record count in dir FCB LD (HL), A LD BC, 0x3 ; (reccnt-extnum) ADD HL, BC EX DE, HL ADD HL, BC LD A, (HL) ; get from user FCB LD (DE), A ; set to directory FCB bdm_endmerge: LD A, 0xff LD (CPM_VARS.bdos_fcb_closed), A ; set was open and closed flag JP bdos_seek_copy ; update directory ; set return status and return bdm_mergerr: LD HL, CPM_VARS.bdos_aret DEC (HL) RET ; ------------------------------------------------------- ; Create a new file by creating a directory entry ; then opening the file ; ------------------------------------------------------- bdos_make: CALL bdos_check_write ; check Write Protection LD HL, (CPM_VARS.bdos_info) ; save user FCB PUSH HL ; on stack LD HL, CPM_VARS.bdos_efcb ; empty FCB ; Save FCB address, look for 0xE5 LD (CPM_VARS.bdos_info), HL ; set empty FCB ; search firs empty slot in directory LD C, 0x1 ; compare one byte 0xE5 CALL bdos_search CALL bdos_end_of_dir ; found flag POP HL LD (CPM_VARS.bdos_info), HL ; restore user FCB address RET Z ; return if not found (no space in dir) EX DE, HL LD HL, FCB_RC ; number or record for this file ADD HL, DE ; clear FCB tail LD C, 17 ; fcblen-namlen XOR A .fcb_set_0: LD (HL), A INC HL DEC C JP NZ, .fcb_set_0 LD HL, FCB_S1 ADD HL, DE LD (HL), A ; Current record within extent? CALL bdos_setcdr CALL bdos_copy_fcb JP bdos_set_fwf ; ------------------------------------------------------- ; Close the current extent, and open the next one to read ; if possible. RMF is true if in read mod ; ------------------------------------------------------- bdos_open_reel: XOR A LD (CPM_VARS.bdos_fcb_closed), A ; clear close flag CALL bdos_close ; close extent CALL bdos_end_of_dir ; check space RET Z ; ret if no more space ; get extent byte from user FCB LD HL, (CPM_VARS.bdos_info) LD BC, FCB_EXT ADD HL, BC LD A, (HL) ; and increment it, mask to 0..31 and store INC A AND MAX_EXT LD (HL), A JP Z, .overflow ; mask extent byte LD B, A LD A, (CPM_VARS.bdos_extmsk) AND B LD HL, CPM_VARS.bdos_fcb_closed AND (HL) JP Z, .read_nxt_extent ; read netx extent JP .extent_in_mem .overflow: LD BC, 0x2 ; S2 ADD HL, BC INC (HL) LD A, (HL) AND MAX_MOD JP Z, .error .read_nxt_extent: LD C, FCB_INFO_LEN CALL bdos_search CALL bdos_end_of_dir JP NZ, .extent_in_mem ; jump if success ; not extent found LD A, (CPM_VARS.bdos_rfm) INC A JP Z, .error ; can not get extent CALL bdos_make ; make new empty dir entry for extent CALL bdos_end_of_dir ; chk no space JP Z, .error ; jmp to error if no space in directory JP .almost_done .extent_in_mem: ; open extent CALL bdos_open_copy .almost_done: CALL bdos_stor_fcb_var ; move updated data to new extent XOR A ; clear error flag JP bdos_ret_a .error: CALL set_ioerr_1 JP bdos_set_fwf ; clear bit 7 in s2 ; ------------------------------------------------------- ; Sequential disk read operation ; ------------------------------------------------------- bdos_seq_disk_read: LD A, 0x1 LD (CPM_VARS.bdos_seqio), A ; drop through to diskread bdos_disk_read: LD A, TRUE LD (CPM_VARS.bdos_rfm), A ; dont allow read unwritten data CALL bdos_stor_fcb_var LD A, (CPM_VARS.bdos_vrecord) ; next record to read LD HL, CPM_VARS.bdos_rcount ; number of records in extent ; in this extent? CP (HL) JP C, .recordok ; no, in next extent, check this exctent fully used CP 128 JP NZ, .error_opn ;open next extent CALL bdos_open_reel XOR A ; reset record number to read LD (CPM_VARS.bdos_vrecord), A ; check open status LD A, (CPM_VARS.bdos_aret) OR A JP NZ, .error_opn .recordok: ; compute block number to read CALL bdos_index ; check if it in bounds CALL bdos_allocated JP Z, .error_opn CALL bdos_atran ; convert to logical sector CALL bdos_seek ; seec track and sector CALL bdos_rdbuff ; read sector JP bdos_set_nxt_rec .error_opn: JP set_ioerr_1 ; ------------------------------------------------------- ; Sequential write disk ; ------------------------------------------------------- bdos_seq_disk_write: LD A, 0x1 LD (CPM_VARS.bdos_seqio), A bdos_disk_write: LD A, FALSE LD (CPM_VARS.bdos_rfm), A ; allow open new extent CALL bdos_check_write ; check write protection LD HL, (CPM_VARS.bdos_info) ; HL -> FCB CALL bdos_check_rofile ; check RO file CALL bdos_stor_fcb_var LD A, (CPM_VARS.bdos_vrecord) ; get record number to write to CP 128 ; lstrec+1 JP NC, set_ioerr_1 ; not in range - error CALL bdos_index ; compute block number CALL bdos_allocated ; check number LD C, 0 JP NZ, .disk_wr1 CALL bdos_dm_position ; get next block number within FCB LD (CPM_VARS.bdos_dminx), A ; and store it LD BC, 0x00 ; start looking for space from start OR A JP Z, .nop_block ; zero - not allocated ; extract previous blk from fcb LD C, A DEC BC CALL bdos_getdm LD B, H LD C, L .nop_block: CALL bdos_get_block ; get next free nearest block LD A, L OR H JP NZ, .block_ok ; error, no more space LD A, 2 JP bdos_ret_a .block_ok: LD (CPM_VARS.bdos_arecord), HL ; set record to access EX DE, HL ; to DE LD HL, (CPM_VARS.bdos_info) ; LD BC, FCB_AL ; 16 ADD HL, BC ; small/big disk LD A, (CPM_VARS.bdos_small_disk) OR A LD A, (CPM_VARS.bdos_dminx) ; rel block JP Z, .alloc_big ; jump for 16bit CALL bdos_hl_add_a ; HL = HL + A LD (HL), E ; save record to access JP .disk_wru .alloc_big: ; save record to acces 16bit LD C, A LD B, 0 ADD HL, BC ADD HL, BC LD (HL), E INC HL LD (HL), D ; Disk write to previously unallocated block .disk_wru: LD C, 2 ; C=2 - write to unused disk space .disk_wr1: LD A, (CPM_VARS.bdos_aret) ; check status OR A RET NZ ; return on error PUSH BC ; store C write flag for BIOS CALL bdos_atran ; convert block number to logical sector LD A, (CPM_VARS.bdos_seqio) ; get access mode (0=random, 1=sequential, 2=special) DEC A DEC A JP NZ, .normal_wr ; special POP BC PUSH BC LD A, C ; A = write status flag (2=write unused space) DEC A DEC A JP NZ, .normal_wr ; fill buffer with zeroes PUSH HL LD HL, (CPM_VARS.bdos_buffa) LD D, A .fill0: LD (HL), A INC HL INC D JP P, .fill0 CALL bdos_setdir ; Tell BIOS buffer address to directory access LD HL, (CPM_VARS.bdos_arecord1) ; get sector that starts current block LD C, 2 ; write status flag (2=write unused space) .fill1: LD (CPM_VARS.bdos_arecord), HL ; set sector to write PUSH BC CALL bdos_seek ; seek track and sector number POP BC CALL bdos_wrbuff ; write zeroes LD HL, (CPM_VARS.bdos_arecord) ; get sector number LD C, 0 ; normal write flag LD A, (CPM_VARS.bdos_blmsk) ; block mask ; write entire block? LD B, A AND L CP B INC HL JP NZ, .fill1 ; continue until (BLKMSK+1) sectors written ; reset sector number POP HL LD (CPM_VARS.bdos_arecord), HL CALL bdos_setdata ; reset DMA address ; Normal write. .normal_wr: CALL bdos_seek ; Set track and sector POP BC ; restore C - write status flag PUSH BC CALL bdos_wrbuff ; write POP BC LD A, (CPM_VARS.bdos_vrecord) ; last file record LD HL, CPM_VARS.bdos_rcount ; last written record CP (HL) JP C, .disk_wr2 ; update record count LD (HL), A INC (HL) LD C, 2 .disk_wr2: DEC C ; patch: NOP DEC C ; patch: NOP JP NZ, .no_update ; patch: LD HL,0 PUSH AF CALL bdos_get_s2 ; set 'extent written to' flag AND 0x7f ; clear 7th bit LD (HL), A POP AF ; record count .no_update: ; it is full CP 127 JP NZ, .set_nxt_rec ; sequential mode? LD A, (CPM_VARS.bdos_seqio) CP 1 JP NZ, .set_nxt_rec CALL bdos_set_nxt_rec ; set next record CALL bdos_open_reel ; get space in dir LD HL, CPM_VARS.bdos_aret ; check status LD A, (HL) OR A JP NZ, .no_space ; no more space DEC A LD (CPM_VARS.bdos_vrecord), A ; -1 .no_space: LD (HL), 0x00 ; clear status .set_nxt_rec: JP bdos_set_nxt_rec ; ------------------------------------------------------- ; Random access seek operation, C=0ffh if read mode ; FCB is assumed to address an active file control block ; (modnum has been set to 1100$0000b if previous bad seek) ; ------------------------------------------------------- bdos_rseek: XOR A LD (CPM_VARS.bdos_seqio), A ; set random mode bdos_rseek1: PUSH BC LD HL, (CPM_VARS.bdos_info) EX DE, HL LD HL, FCB_RN ; Random access record number ADD HL, DE LD A, (HL) AND 7Fh ; [6:0] bits record number to access PUSH AF ; get bit 7 LD A, (HL) RLA INC HL ; get high byte [3:0] bits LD A, (HL) RLA AND 00011111b ; get extent byte LD C, A LD A, (HL) RRA RRA RRA RRA AND 0x0f LD B, A POP AF INC HL ; get next byte LD L, (HL) ; check overflow INC L DEC L LD L, 6 JP NZ, .seek_err ; error 6 if overflow ; save current record in FCB LD HL, FCB_CR ADD HL, DE LD (HL), A ; check extent byte LD HL, FCB_EXT ADD HL, DE LD A, C SUB (HL) JP NZ, .close ; not same as previous ; check extra s2 byte LD HL, FCB_S2 ADD HL, DE LD A, B SUB (HL) AND 0x7f JP Z, .seek_ok ; same, is ok .close: PUSH BC PUSH DE CALL bdos_close ; close current extent POP DE POP BC LD L, 3 ; Cannot close error #3 LD A, (CPM_VARS.bdos_aret) ; check status INC A JP Z, .bad_seek ; status != 0 - error ; set extent byte to FCB LD HL, FCB_EXT ADD HL, DE LD (HL), C ; set S2 - extra extent byte LD HL, FCB_S2 ADD HL, DE LD (HL), B CALL open LD A, (CPM_VARS.bdos_aret) INC A JP NZ, .seek_ok POP BC PUSH BC LD L, 0x4 ; Seek to unwritten extent #4 INC C JP Z, .bad_seek CALL bdos_make LD L, 0x5 ; Cannot create new extent #5 LD A, (CPM_VARS.bdos_aret) INC A JP Z, .bad_seek .seek_ok: POP BC XOR A JP bdos_ret_a .bad_seek: PUSH HL CALL bdos_get_s2 LD (HL), 11000000b POP HL .seek_err: POP BC LD A,L LD (CPM_VARS.bdos_aret), A JP bdos_set_fwf ; ------------------------------------------------------- ; Random disk read operation ; ------------------------------------------------------- bdos_rand_disk_read: LD C, 0xff ; mode CALL bdos_rseek ; positioning CALL Z, bdos_disk_read ; read if ok RET ; ------------------------------------------------------- ; Random disk write operation ; ------------------------------------------------------- bdos_rand_disk_write: LD C, 0 ; mode CALL bdos_rseek ; positioning CALL Z, bdos_disk_write ; read if ok RET ; ------------------------------------------------------- ; Compute random record position ; Inp: HL -> FCB ; DE - relative location of record number ; Out: C - r0 byte ; B - r1 byte ; A - r2 byte ; ZF set - Ok, reset - overflow ; ------------------------------------------------------- bdos_compute_rr: EX DE, HL ; DE -> FCB ADD HL, DE ; HL = FCB+DE LD C, (HL) ; get record number LD B, 0 ; in BC LD HL, FCB_EXT ADD HL, DE LD A, (HL) ; A = extent ; calculate BC = recnum + extent*128 RRCA ; A[0] -> A[7] AND 0x80 ; ignore other bits ADD A, C ; add to record number LD C, A ; C=r0 LD A, 0 ADC A, B LD B, A ; B=r1 ; LD A, (HL) ; A = extent RRCA AND 0x0f ; A = EXT[4:1] ADD A, B ; add to r1 byte LD B, A LD HL, FCB_S2 ADD HL, DE LD A, (HL) ; A = extra extent bits ADD A, A ; *2 ADD A, A ; *4 ADD A, A ; *8 ADD A, A ; *16 PUSH AF ; save flags (need only CF) ADD A, B ; add to r1 LD B, A PUSH AF ; save flags POP HL ; flags on L LD A, L ; A bit 0 - CF POP HL OR L ; merge both carry flags AND 0x1 ; mask only CF RET ; ------------------------------------------------------- ; Compute logical file size for current FCB. ; Setup r0, r1, r2 bytes - maximum record number ; ------------------------------------------------------- bdos_get_file_size: LD C, FN_LEN CALL bdos_search ; get first dir record (first extent) LD HL, (CPM_VARS.bdos_info) ; HL -> FCB ; zeroing r0, r1, r2 LD DE, FCB_RN ; D=0 ADD HL, DE PUSH HL LD (HL), D INC HL LD (HL), D INC HL LD (HL), D ; .get_extent: CALL bdos_end_of_dir JP Z, .done ; if not more extents found, return CALL bdos_getdptra ; HL -> FCB LD DE, FCB_RC CALL bdos_compute_rr ; compute random access parameters POP HL PUSH HL ; Now let's compare these values ​​with those indicated LD E, A LD A, C SUB (HL) INC HL LD A, B SBC A, (HL) INC HL LD A, E SBC A, (HL) JP C, .less_size ; found larger extent (size), save it to fcb LD (HL), E DEC HL LD (HL), B DEC HL LD (HL), C .less_size: CALL bdos_search_nxt JP .get_extent .done: POP HL RET ; ------------------------------------------------------- ; (F_RANDREC) - Update random access pointer ; Set the random record count bytes of the FCB to the number ; of the last record read/written by the sequential I/O calls. ; ------------------------------------------------------- bdos_set_random: LD HL, (CPM_VARS.bdos_info) ; HL -> FCB LD DE, FCB_CR ; Current record within extent CALL bdos_compute_rr ; Compute random access parameters LD HL, FCB_RN ADD HL, DE ; HL -> Random access record number ; set r0, r1, r2 LD (HL), C INC HL LD (HL), B INC HL LD (HL), A RET ; ------------------------------------------------------- ; Select disk to bdos_curdsk for subsequent input or ; output ops ; ------------------------------------------------------- bdos_select: LD HL, (CPM_VARS.bdos_dlog) ; login vector LD A, (CPM_VARS.bdos_curdsk) ; current drive LD C, A CALL bdos_hlrotr ; shift active bit for this drive PUSH HL EX DE, HL CALL bdos_sel_dsk ; select drive POP HL CALL Z, bdos_sel_error ; check for error drive ; new active drive? LD A, L RRA RET C ; no, return ; yes, update login vector LD HL, (CPM_VARS.bdos_dlog) LD C, L LD B, H CALL bdos_set_dsk_bit ; set bits in vector LD (CPM_VARS.bdos_dlog), HL ; store new vector JP bdos_initialize ; ------------------------------------------------------- ; Select disc ; Inp: C=0x0E ; E=drive number 0-A, 1-B .. 15-P ; Out: L=A=0 - ok or 0xFF - error ; ------------------------------------------------------- bdos_select_disk: ; check disk change LD A, (CPM_VARS.bdos_linfo) LD HL, CPM_VARS.bdos_curdsk CP (HL) RET Z ; is same, return ; login new disk LD (HL), A JP bdos_select ; ------------------------------------------------------- ; Auto select disk by FCB_DR ; ------------------------------------------------------- bdos_reselect: LD A, TRUE LD (CPM_VARS.bdos_resel), A ; set flag LD HL, (CPM_VARS.bdos_info) ; HL -> FCB LD A, (HL) ; get specified disk AND 0x1f ; A = A[4:0] DEC A ; adjust for 0-A, 1-B and so on LD (CPM_VARS.bdos_linfo), A ; set as parameter CP 0x1e ; no change? JP NC, .no_select ; change, but save current to old LD A, (CPM_VARS.bdos_curdsk) LD (CPM_VARS.bdos_olddsk), A ; save FCB_DR byte LD A, (HL) LD (CPM_VARS.bdos_fcbdsk), A ; clear FCB_DR[4:0] bits - user code AND 11100000b LD (HL), A CALL bdos_select_disk .no_select: ; set user to FCB_DR low bits LD A, (CPM_VARS.bdos_userno) LD HL, (CPM_VARS.bdos_info) OR (HL) LD (HL), A RET ; ------------------------------------------------------- ; Return version number ; ------------------------------------------------------- bdos_get_version: LD A, CPM_VERSION ; 0x22 - v2.2 JP bdos_ret_a ; ------------------------------------------------------- ; Reset disk system - initialize to disk 0 ; ------------------------------------------------------- bdos_reset_disks: LD HL, 0x00 LD (CPM_VARS.bdos_rodsk), HL ; clear ro flags LD (CPM_VARS.bdos_dlog), HL ; clear login vector XOR A ; disk 0 LD (CPM_VARS.bdos_curdsk), A ; set disk 'A' as current LD HL, dma_buffer ; set 'DMA' buffer address to default LD (CPM_VARS.bdos_dmaad), HL CALL bdos_setdata JP bdos_select ; select drive 'A' ; ------------------------------------------------------- ; Open file ; Inp: DE -> FCB ; Out: BA and HL - error. ; ------------------------------------------------------- bdos_open_file: CALL bdos_clear_s2 CALL bdos_reselect JP open ; ------------------------------------------------------- ; Close file ; Inp: DE -> FCB ; Out: BA and HL - error. ; ------------------------------------------------------- bdos_close_file: CALL bdos_reselect JP bdos_close ; ------------------------------------------------------- ; Search for first occurrence of a file in directory ; Inp: DE -> FCB ; Out: BA and HL - error. ; ------------------------------------------------------- bdos_search_first: LD C, 0x00 ; special search EX DE, HL LD A, (HL) CP '?' ; '?' is first byte? JP Z, .qselect ; return first matched entry CALL bdos_getexta ; get extent byte from FCB LD A, (HL) CP '?' ; it is '?' CALL NZ, bdos_clear_s2 ; set S2 to 0 if not CALL bdos_reselect ; select disk by FCB LD C, FCB_INFO_LEN ; match 1first 15 bytes .qselect: CALL bdos_search ; search first entry JP bdos_dir_to_user ; move entry to user space ; ------------------------------------------------------- ; Search for next occurrence of a file name ; Out: BA and HL - error. ; ------------------------------------------------------- bdos_search_next: LD HL, (CPM_VARS.bdos_searcha) ; restore FCB address LD (CPM_VARS.bdos_info), HL ; set as parameter CALL bdos_reselect ; select drive by FCB CALL bdos_search_nxt ; search next JP bdos_dir_to_user ; move entry to user space ; ------------------------------------------------------- ; Remove directory ; Inp: DE -> FCB ; Out: BA and HL - error. ; ------------------------------------------------------- bdos_rm_file: CALL bdos_reselect ; Select drive by FCB CALL bdos_era_file ; Delete file JP bdos_ret_dirloc ; Return directory location in A ; ------------------------------------------------------- ; Read next 128b record ; Inp: DE -> FCB ; Out: BA and HL - error. ; A=0 - Ok, ; 1 - end of file, ; 9 - invalid FCB, ; 10 - media changed, ; 0FFh - hardware error. ; ------------------------------------------------------- bdos_read_file: CALL bdos_reselect ; select drive JP bdos_seq_disk_read ; and read ; ------------------------------------------------------- ; Write next 128b record ; Inp: DE -> FCB ; Out: BA and HL - error. ; A=0 - Ok, ; 1 - directory full, ; 2 - disc full, ; 9 - invalid FCB, ; 10 - media changed, ; 0FFh - hardware error. ; ------------------------------------------------------- bdos_write_file: CALL bdos_reselect ; select drive JP bdos_seq_disk_write ; and write ; ------------------------------------------------------- ; Create file ; Inp: DE -> FCB. ; Out: Error in BA and HL ; A=0 - Ok, ; 0FFh - directory is full. ; ------------------------------------------------------- bdos_make_file: CALL bdos_clear_s2 ; clear S2 byte CALL bdos_reselect ; select drive JP bdos_make ; and make file ; ------------------------------------------------------- ; Rename file. New name, stored at FCB+16 ; Inp: DE -> FCB. ; Out: Error in BA and HL ; A=0-3 if successful; ; A=0FFh if error. ; ------------------------------------------------------- bdos_ren_file: CALL bdos_reselect ; select drive CALL bdos_rename ; rename file JP bdos_ret_dirloc ; Return directory location in A ; ------------------------------------------------------- ; Return bitmap of logged-in drives ; Out: bitmap in HL. ; ------------------------------------------------------- bdos_get_login_vec: LD HL, (CPM_VARS.bdos_dlog) JP sthl_ret ; ------------------------------------------------------- ; Return current drive ; Out: A - currently selected drive. 0 => A:, 1 => B: etc. ; ------------------------------------------------------- bdos_get_cur_drive: LD A, (CPM_VARS.bdos_curdsk) JP bdos_ret_a ; ------------------------------------------------------- ; Set DMA address ; Inp: DE - address of DMA buffer ; ------------------------------------------------------- bdos_set_dma_addr: EX DE, HL LD (CPM_VARS.bdos_dmaad), HL JP bdos_setdata ; ------------------------------------------------------- ; fn27: Return the address of cur disk allocation map ; Out: HL - address ; ------------------------------------------------------- bdos_get_alloc_addr: LD HL, (CPM_VARS.bdos_alloca) JP sthl_ret ; ------------------------------------------------------- ; Get write protection status ; ------------------------------------------------------- bdos_get_wr_protect: LD HL, (CPM_VARS.bdos_rodsk) JP sthl_ret ; ------------------------------------------------------- ; Set file attributes (ro, system) ; ------------------------------------------------------- bdos_set_attr: CALL bdos_reselect CALL bdos_update_attrs JP bdos_ret_dirloc ; ------------------------------------------------------- ; Return address of disk parameter block of the ; current drive ; ------------------------------------------------------- bdos_get_dpb: LD HL, (CPM_VARS.bdos_dpbaddr) ; ------------------------------------------------------- ; Common code to return Value from BDOS functions ; ------------------------------------------------------- sthl_ret: LD (CPM_VARS.bdos_aret), HL RET ; ------------------------------------------------------- ; Get/set user number ; Inp: E - 0..15 - set user. 0xFF - get user ; Out: A - returns user number ; ------------------------------------------------------- bdos_set_user: LD A, (CPM_VARS.bdos_linfo) CP 0xff JP NZ, .set_user LD A, (CPM_VARS.bdos_userno) JP bdos_ret_a .set_user: AND 0x1f ; mask for 0..31 but will be < 16 LD (CPM_VARS.bdos_userno), A RET ; ------------------------------------------------------- ; Random read. Record specified in the random record ; count area of the FCB, at the DMA address ; Inp: DE -> FCB ; Out: Error codes in BA and HL. ; ------------------------------------------------------- bdos_rand_read: CALL bdos_reselect ; select drive by FCB JP bdos_rand_disk_read ; and read ; ------------------------------------------------------- ; Random access write record. ; Record specified in the random record count area of the FCB, at the DMA address ; Inp: DE -> FCB ; Out: Error codes in BA and HL. ; ------------------------------------------------------- bdos_rand_write: CALL bdos_reselect ; select drive by FCB JP bdos_rand_disk_write ; and write ; ------------------------------------------------------- ; Compute file size. ; Set the random record count bytes of the FCB to the ; number of 128-byte records in the file. ; ------------------------------------------------------- bdos_compute_fs: CALL bdos_reselect ; select drive by FCB JP bdos_get_file_size ; and get file size ; ------------------------------------------------------- ; Selectively logoff (reset) disc drives ; Out: A=0 - Ok, 0xff if error ; ------------------------------------------------------- bdos_reset_drives: LD HL, (CPM_VARS.bdos_info) ; HL - drives to logoff map LD A, L CPL LD E, A LD A, H CPL LD HL, (CPM_VARS.bdos_dlog) ; get vector AND H ; reset in hi byte LD D, A ; and store in D LD A, L ; reset low byte AND E LD E, A ; and store in E LD HL, (CPM_VARS.bdos_rodsk) ; HL - disk RO map EX DE, HL LD (CPM_VARS.bdos_dlog), HL ; store new login vector ; reset RO flags LD A, L AND E LD L, A LD A, H AND D LD H, A ; and store new value LD (CPM_VARS.bdos_rodsk), HL RET ; ------------------------------------------------------- ; Reach this point at the end of processing ; to return the data to the user. ; ------------------------------------------------------- bdos_goback: LD A, (CPM_VARS.bdos_resel) ; check for selection drive by user FCB OR A JP Z, bdos_ret_mon ; return if not selected LD HL, (CPM_VARS.bdos_info) ; HL -> FCB_DR LD (HL), 0 LD A, (CPM_VARS.bdos_fcbdsk) OR A ; restore drive JP Z, bdos_ret_mon ; return if default LD (HL), A ; restore LD A, (CPM_VARS.bdos_olddsk) ; previous drive LD (CPM_VARS.bdos_linfo), A ; set parameter CALL bdos_select_disk ; select previous drive ; ------------------------------------------------------- ; Return from the disk monitor ; ------------------------------------------------------- bdos_ret_mon: LD HL, (CPM_VARS.bdos_usersp) ; return user stack LD SP, HL LD HL, (CPM_VARS.bdos_aret) ; get return value LD A, L ; ver 1.4 CP/M compatibility LD B, H RET ; ------------------------------------------------------- ; Random disk write with zero fill of ; unallocated block ; ------------------------------------------------------- bdos_rand_write_z: CALL bdos_reselect ; select drive LD A, 2 ; special write mode LD (CPM_VARS.bdos_seqio), A LD C, FALSE ; write indicator CALL bdos_rseek1 ; seek position in a file CALL Z, bdos_disk_write ; write if no errors RET ; ------------------------------------------------------- ; Unuser initialized data ; ------------------------------------------------------- filler: DB 0xF1, 0xE1 ; POP AF, POP HL ; ------------------------------------------------------- ; Filler to align blocks in ROM ; ------------------------------------------------------- LAST EQU $ CODE_SIZE EQU LAST-0xC800 FILL_SIZE EQU 0xE00-CODE_SIZE DISPLAY "| BDOS\t| ",/H,bdos_start," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" ASSERT bdos_rand_write_z = 0xd55e FILLER DS FILL_SIZE, 0xFF ENDMODULE IFNDEF BUILD_ROM OUTEND ENDIF