OK240.2-Firmware/CPM_v2.2_r7_b89a7e16/ccp_ram.asm

1528 lines
40 KiB
NASM

; ======================================================
; Ocean-240.2
; CP/M CCP (Console Command Processor) Resident part
; ORG C000 at RPM, moved to B200-BA09
;
; Disassembled by Romych 2026-02-01
; ======================================================
INCLUDE "equates.inc"
INCLUDE "ram.inc"
IFNDEF BUILD_ROM
OUTPUT ccp_ram.bin
ENDIF
MODULE CCP_RAM
ORG 0xB200
ccp_ram_ent:
JP ccp_ent
JP ccp_clear_buff
ccp_inbuff:
DB 0x7F, 0x00
ccp_inp_line:
DB ASCII_SP, ASCII_SP
msg_copyright:
DB " COPYRIGHT (C) 1979, DIGITAL RESEARCH ", 0x00
DS 73, 0x0
ccp_inp_line_addr:
DW ccp_inp_line
cur_name_ptr:
DW 0h
; ---------------------------------------------------
; Call BDOS function 2 (C_WRITE) - Console output
; Inp: A - char to output
; ---------------------------------------------------
ccp_print:
LD E, A
LD C, 2
JP jp_bdos_enter
; ---------------------------------------------------
; Put char to console
; Inp: A - char
; ---------------------------------------------------
ccp_putc:
PUSH BC
CALL ccp_print
POP BC
RET
ccp_out_crlf:
LD A, ASCII_CR
CALL ccp_putc
LD A, ASCII_LF
JP ccp_putc
ccp_out_space:
LD A,' '
JP ccp_putc
; ---------------------------------------------------
; Out message from new line
; Inp: BC -> Message
; ---------------------------------------------------
ccp_out_crlf_msg:
PUSH BC
CALL ccp_out_crlf
POP HL
; ---------------------------------------------------
; Out asciiz message
; Inp: HL -> Message
; ---------------------------------------------------
ccp_out_msg:
LD A, (HL) ;= "READ ERROR"
OR A
RET Z
INC HL
PUSH HL
CALL ccp_print
POP HL
JP ccp_out_msg
; ---------------------------------------------------
; Call BDOS function 13 (DRV_ALLRESET) - Reset discs
; ---------------------------------------------------
ccp_bdos_drv_allreset:
LD C,13
JP jp_bdos_enter
; ---------------------------------------------------
; Call BDOS function 14 (DRV_SET) - Select disc
; Inp: A - disk
; ---------------------------------------------------
ccp_bdos_drv_set:
LD E, A
LD C, 14
JP jp_bdos_enter
; ---------------------------------------------------
; Call BDOS fn and return result
; Inp: C - fn no
; Out: A - error + 1
; ---------------------------------------------------
ccp_call_bdos:
CALL jp_bdos_enter
LD (ccp_bdos_result_code), A
INC A
RET
; ---------------------------------------------------
; BDOS function 15 (F_OPEN) - Open file /Dir
; Inp: DE -> FCB
; Out: A=0 for error, or 1-4 for success
; ---------------------------------------------------
ccp_bdos_open_f:
LD C, 15
JP ccp_call_bdos
; ---------------------------------------------------
; Open file by current FCB
; ---------------------------------------------------
ccp_open_cur_fcb:
XOR A
LD (ccp_current_fcb_cr), A ; clear current record counter
LD DE, ccp_current_fcb
JP ccp_bdos_open_f
; ---------------------------------------------------
; BDOS function 16 (F_CLOSE) - Close file
; ---------------------------------------------------
ccp_bdos_close_f:
LD C, 16
JP ccp_call_bdos
; ---------------------------------------------------
; Call BDOS function 17 (F_SFIRST) - search for first
; Out: A = 0 in error, 1-4 if success
; ---------------------------------------------------
ccp_bdos_find_first:
LD C, 17
JP ccp_call_bdos
; ---------------------------------------------------
; Call BDOS function 18 (F_SNEXT) - search for next
; Out: A = 0 in error, 1-4 if success
; ---------------------------------------------------
ccp_bdos_find_next:
LD C, 18
JP ccp_call_bdos ; BDOS 18 (F_SNEXT) - search for next ?
; ---------------------------------------------------
; Call BDOS F_FIRST with current FCB
; ---------------------------------------------------
ccp_find_first:
LD DE, ccp_current_fcb
JP ccp_bdos_find_first
; ---------------------------------------------------
; Call BDOS function 19 (F_DELETE) - delete file
; ---------------------------------------------------
ccp_bdos_era_file:
LD C,19
JP jp_bdos_enter
; ---------------------------------------------------
; Call BDOS and set ZF by result
; ---------------------------------------------------
ccp_bdos_enter_zf:
CALL jp_bdos_enter
OR A
RET
; ---------------------------------------------------
; Read next 128 bytes of file
; Inp: DE -> FCB
; Out: a = 0 - ok;
; 1 - EOF;
; 9 - invalid FCB;
; 10 - Media changed;
; 0xFF - HW error.
; ---------------------------------------------------
ccp_bdos_read_f:
LD C, 20
JP ccp_bdos_enter_zf
; ---------------------------------------------------
; Read file by current FCB
; ---------------------------------------------------
ccp_read_f_fcb:
LD DE, ccp_current_fcb
JP ccp_bdos_read_f
; ---------------------------------------------------
; Call BDOS function 21 (F_WRITE) - write next record
; ---------------------------------------------------
ccp_bdos_f_write:
LD C, 21
JP ccp_bdos_enter_zf
; ---------------------------------------------------
; Call BDOS function 22 (F_MAKE) - create file
; ---------------------------------------------------
ccp_bdos_make_f:
LD C, 22
JP ccp_call_bdos
; ---------------------------------------------------
; Call BDOS function 23 (F_RENAME) - Rename file
; ---------------------------------------------------
ccp_bdos_rename_f:
LD C, 23
JP jp_bdos_enter
; ---------------------------------------------------
; Call BDOS function 32 (F_USERNUM) for get user number
; Out: A - user no
; ---------------------------------------------------
ccp_bdos_get_user:
LD E, 0xff
; ---------------------------------------------------
; Call BDOS function 32 (F_USERNUM) set user number
; Inp: E - user no 0-15, or 0xFF for get user
; Out: A - user no
; ---------------------------------------------------
ccp_bdos_set_user:
LD C, 32
JP jp_bdos_enter
; ---------------------------------------------------
; Get user no and store in upper nibble of sys var
; ---------------------------------------------------
ccp_set_cur_drv:
CALL ccp_bdos_get_user
ADD A, A
ADD A, A
ADD A, A
ADD A, A ; user no at upper nibble
LD HL, ccp_cur_drive
OR (HL)
LD (cur_user_drv), A
RET
; ---------------------------------------------------
; Replace new drive in lower nibble of sys var
; reset user no
; Out: A - drive no
; ---------------------------------------------------
ccp_reset_cur_drv:
LD A, (ccp_cur_drive)
LD (cur_user_drv), A
RET
; ---------------------------------------------------
; Uppercase character a..z
; Inp: A - character
; Out: A - character in upper case
; ---------------------------------------------------
char_to_upper:
CP 'a'
RET C
CP '{'
RET NC
; character in a..z range
AND 0x5f
RET
; ---------------------------------------------------
; Get user input from console or from batch
; file $$$.SUB
; ---------------------------------------------------
ccp_get_inp:
LD A, (ccp_batch)
OR A
JP Z, ccp_gin_no_batch
; select drive A for $$$.SUB
LD A, (ccp_cur_drive)
OR A
LD A, 0x0
CALL NZ, ccp_bdos_drv_set
; open batch file
LD DE, ccp_batch_fcb
CALL ccp_bdos_open_f
JP Z, ccp_gin_no_batch ; go to con inp if file not found
; read last record
LD A, (ccp_batch_fcb_rc)
DEC A
LD (ccp_batch_fcb_cr), A
LD DE, ccp_batch_fcb
CALL ccp_bdos_read_f
JP NZ, ccp_gin_no_batch ; stop on EOF
; move data from file to buffer
LD DE, ccp_inbuff+1
LD HL, dma_buffer
LD B, DMA_BUFF_SIZE ; 0x80
CALL ccp_mv_hlde_b
LD HL, ccp_batch_fcb_s2
LD (HL), 0x0
INC HL
DEC (HL) ; decriment record count
; close batch file
LD DE, ccp_batch_fcb
CALL ccp_bdos_close_f
JP Z, ccp_gin_no_batch
; reselect old drive if not 0
LD A, (ccp_cur_drive)
OR A
CALL NZ, ccp_bdos_drv_set
; print command line
LD HL, ccp_inp_line ; ccp_inbuff+2
CALL ccp_out_msg
CALL ccp_getkey_no_wait
JP Z, ccp_gin_nokey
; terminate batch processing on any key
CALL ccp_del_batch
JP ccp_get_command
; get user input from keyboard
ccp_gin_no_batch:
CALL ccp_del_batch
CALL ccp_set_cur_drv
LD C, 10
LD DE, ccp_inbuff
; Call BDOS C_READSTR DE -> inp buffer
CALL jp_bdos_enter
CALL ccp_reset_cur_drv
ccp_gin_nokey:
LD HL, ccp_inbuff+1
LD B, (HL)
ccp_gin_uppr:
INC HL
LD A, B
OR A
JP Z, ccp_gin_uppr_end
LD A, (HL) ;= 2020h
CALL char_to_upper
LD (HL), A ;= 2020h
DEC B
JP ccp_gin_uppr
ccp_gin_uppr_end:
LD (HL), A ; set last character to 0
LD HL, ccp_inp_line ;
LD (ccp_inp_line_addr), HL ;
RET
; ---------------------------------------------------
; Check keyboard
; Out: A - pressed key code
; ZF set if no key pressed
; ---------------------------------------------------
ccp_getkey_no_wait:
LD C,11
; Call BDOS (C_STAT) - Console status
CALL jp_bdos_enter
OR A
RET Z ; ret if no character waiting
LD C,1
; Call BDOS (C_READ) - Console input
CALL jp_bdos_enter
OR A
RET
; ---------------------------------------------------
; Call BDOS function 25 (DRV_GET) - Return current drive
; Out: A - drive 0-A, 1-B...
; ---------------------------------------------------
ccp_bdos_drv_get:
LD C, 25
JP jp_bdos_enter
; ---------------------------------------------------
; Set disk buffer address to default buffer
; ---------------------------------------------------
cpp_set_disk_buff_addr:
LD DE, dma_buffer
; ---------------------------------------------------
; Call BDOS function 26 (F_DMAOFF) - Set DMA address
; Inp: DE - address
; ---------------------------------------------------
ccp_bdos_dma_set:
LD C, 26
JP jp_bdos_enter
; ---------------------------------------------------
; Delete batch file created by submit
; ---------------------------------------------------
ccp_del_batch:
LD HL, ccp_batch
LD A, (HL)
OR A
RET Z ; return if no active batch file
LD (HL), 0x0 ; mark as inactive
XOR A
CALL ccp_bdos_drv_set ; select drive 0
LD DE, ccp_batch_fcb
CALL ccp_bdos_era_file
LD A, (ccp_cur_drive)
JP ccp_bdos_drv_set
; --------------------------------------------------
; Check "serial number" of CP/M
; --------------------------------------------------
ccp_verify_pattern:
LD DE, cpm_pattern ; = F9h
LD HL, 0x0000
LD B, 6
ccp_chk_patt_nex:
LD A, (DE)
CP (HL)
NOP ; JP NZ HALT was here
NOP
NOP
INC DE
INC HL
DEC B
JP NZ, ccp_chk_patt_nex
RET
; --------------------------------------------------
; Print syntax error indicator
; --------------------------------------------------
print_syn_err:
CALL ccp_out_crlf
LD HL, (cur_name_ptr)
pse_next:
LD A, (HL)
CP ASCII_SP
JP Z, pse_end
OR A
JP Z, pse_end
PUSH HL
CALL ccp_print
POP HL
INC HL
JP pse_next
pse_end:
LD A, '?'
CALL ccp_print
CALL ccp_out_crlf
CALL ccp_del_batch
JP ccp_get_command
; --------------------------------------------------
; Check user input characters for legal range
; Inp: [DE] - pointer to character
; --------------------------------------------------
cpp_valid_inp:
LD A, (DE)
OR A
RET Z
CP ASCII_SP ; >= Space
JP C, print_syn_err
RET Z
CP '='
RET Z
CP '_'
RET Z
CP '.'
RET Z
CP ':'
RET Z
CP ';'
RET Z
CP '<'
RET Z
CP '>'
RET Z
RET
; ---------------------------------------------------
; Find non space character until end
; Inp: DE -> current character
; Out: DE -> non space
; A - character
; ZF set on EOL
; ---------------------------------------------------
ccp_find_no_space:
LD A, (DE)
OR A
RET Z
CP ASCII_SP
RET NZ
INC DE
JP ccp_find_no_space
; ---------------------------------------------------
; HL=HL+A
; ---------------------------------------------------
sum_hl_a:
ADD A, L
LD L, A
RET NC
INC H ; inc H if CF is set
RET
; ---------------------------------------------------
; Convert first name to fcb
; ---------------------------------------------------
ccp_cv_first_to_fcb:
LD A, 0x0
; Convert filename from cmd to fcb
; replace '*' to '?'
; Inp: A - offset in fcb filename
; Out: A - count of '?' in file name
; ZF is set for fegular file name
ccp_cv_fcb_filename:
LD HL, ccp_current_fcb
CALL sum_hl_a
PUSH HL
PUSH HL
XOR A
LD (ccp_chg_drive), A
LD HL, (ccp_inp_line_addr) ; HL -> input line
EX DE, HL
CALL ccp_find_no_space ; get next non blank char
EX DE, HL
LD (cur_name_ptr), HL ; save name ptr
EX DE, HL
POP HL
LD A, (DE) ; load first name char
OR A
JP Z, cur_cvf_n_end
SBC A, 'A'-1 ; 0x40 for drive letter
LD B, A
INC DE
LD A, (DE)
CP ':' ; is ':' after drive letter?
JP Z, cur_cvf_drv_ltr
DEC DE ; no, step back
cur_cvf_n_end:
LD A, (ccp_cur_drive)
LD (HL), A
JP cur_cvf_basic_fn
cur_cvf_drv_ltr:
LD A, B
LD (ccp_chg_drive), A ; set change drive flag
LD (HL), B
INC DE
cur_cvf_basic_fn:
LD B, 8 ; file name length
cur_cvf_chr_nxt:
CALL cpp_valid_inp
JP Z, cur_cvf_sp_remains
INC HL
CP '*'
JP NZ, cur_cvf_no_star
LD (HL), '?'
JP cur_cvf_nxt1
cur_cvf_no_star:
LD (HL), A
INC DE
cur_cvf_nxt1:
DEC B
JP NZ, cur_cvf_chr_nxt
cur_cvf_nxt_delim:
CALL cpp_valid_inp
JP Z, cur_cvf_ext
INC DE
JP cur_cvf_nxt_delim
; fill remains with spaces
cur_cvf_sp_remains:
INC HL
LD (HL), ASCII_SP
DEC B
JP NZ, cur_cvf_sp_remains
; file name extension
cur_cvf_ext:
LD B, 3
CP '.'
JP NZ, cur_cvf_ext_fill_sp
INC DE
; handle current ext char
cur_cvf_ext_nxt:
CALL cpp_valid_inp
JP Z, cur_cvf_ext_fill_sp
INC HL
; change * to ?
CP '*'
JP NZ, cur_cvf_no_star2
LD (HL), '?'
JP cur_cvf_nxt2
cur_cvf_no_star2:
LD (HL), A
INC DE
cur_cvf_nxt2:
DEC B
JP NZ, cur_cvf_ext_nxt
cur_cvf_ext_skip:
CALL cpp_valid_inp
JP Z, cur_cvf_rst_attrs
INC DE
JP cur_cvf_ext_skip
; skip remains ext pos with dpaces
cur_cvf_ext_fill_sp:
INC HL
LD (HL), ASCII_SP
DEC B
JP NZ, cur_cvf_ext_fill_sp
; set fcb extent, res1, res2 to 0
cur_cvf_rst_attrs:
LD B, 3
cur_cvf_attrs_nxt:
INC HL
LD (HL), 0x0
DEC B
JP NZ, cur_cvf_attrs_nxt
EX DE, HL
LD (ccp_inp_line_addr), HL ; save input line pointer
; Check for ambigeous file name
POP HL ; -> file name in fcb
LD BC, 0x000b ; b=0, c=11
cur_cvf_ambig:
INC HL
LD A, (HL)
CP '?'
JP NZ, cur_cvf_ambig_nxt1
INC B
cur_cvf_ambig_nxt1:
DEC C
JP NZ, cur_cvf_ambig
LD A, B ; a = count of '?'
OR A ; set ZF if regular filename
RET
; ---------------------------------------------------
; CP/M command table
; ---------------------------------------------------
cpm_cmd_tab:
DB "$DIR"
DB "ERA "
DB "TYPE"
DB "SAVE"
DB "REN "
DB "USER"
cpm_pattern:
DB 0xF9, 0x16, 0, 0, 0, 0x6B ; CP/M serial number?
;LD SP, HL
;LD D, 0x00
;NOP
;NOP
;LD L,E
; ---------------------------------------------------
; Search for CP/M command
; Out: A - command number
; ---------------------------------------------------
ccp_search_cmd:
LD HL, cpm_cmd_tab
LD C, 0x0
ccp_sc_cmd_nxt:
LD A, C
CP CCP_COMMAND_CNT ; 6
RET NC
LD DE, ccp_current_fcb_fn ; fcb filename
LD B, CCP_COMMAND_LEN ; max cmd len
ccp_sc_chr_nxt:
LD A, (DE)
CP (HL) ; compate fcb fn and command table
JP NZ, ccp_sc_no_match ; cmd not match
INC DE
INC HL
DEC B
JP NZ, ccp_sc_chr_nxt
; last can be space for 3-letter commands
LD A, (DE)
CP ASCII_SP
JP NZ, ccp_sc_skip_cmd
LD A, C ; return command number in A
RET
; skip remains
ccp_sc_no_match:
INC HL
DEC B
JP NZ, ccp_sc_no_match
; go to next cmd
ccp_sc_skip_cmd:
INC C
JP ccp_sc_cmd_nxt
; --------------------------------------------------
; Clear command buffer and go to command processor
; --------------------------------------------------
ccp_clear_buff:
XOR A
LD (ccp_inbuff+1), A ; actual buffer len = 0
; --------------------------------------------------
; Entrance to CCP
; Inp: C - current user * 16
; --------------------------------------------------
@ccp_ent:
LD SP, ccp_stack
PUSH BC ;
LD A, C
; / 16
RRA
RRA
RRA
RRA
; cur user no in low nibble
AND 0x0f ; user 0..15
LD E, A
CALL ccp_bdos_set_user
CALL ccp_bdos_drv_allreset
LD (ccp_batch), A
POP BC
LD A, C ; a = user*16
AND 0xf ; low nibble - drive
LD (ccp_cur_drive), A
CALL ccp_bdos_drv_set
LD A, (ccp_inbuff+1)
OR A
JP NZ, ccp_process_cmd
; --------------------------------------------------
; Out prompt and get user command from console
; --------------------------------------------------
ccp_get_command:
LD SP, ccp_stack ; reset stack pointer
CALL ccp_out_crlf ; from new line
CALL ccp_bdos_drv_get
ADD A, 65 ; convert drive no to character
CALL ccp_print ; print current drive letter
LD A, '>' ; and prompt
CALL ccp_print
CALL ccp_get_inp ; and wait string
; --------------------------------------------------
; Process command
; --------------------------------------------------
ccp_process_cmd:
LD DE, dma_buffer
CALL ccp_bdos_dma_set ; setup buffer
CALL ccp_bdos_drv_get
LD (ccp_cur_drive), A ; store cur drive
CALL ccp_cv_first_to_fcb ; convert first command parameter to fcb
CALL NZ, print_syn_err ; if wildcard, out error message
LD A, (ccp_chg_drive) ; check drive change flag
OR A
JP NZ, ccp_unk_cmd ; if drive changed, handle as unknown command
CALL ccp_search_cmd ; ret A = command number
LD HL, ccp_cmd_addr
; DE = command number
LD E, A
LD D, 0x0
ADD HL, DE
ADD HL, DE ; HL = HL + 2*command_number
LD A, (HL)
INC HL
LD H, (HL)
LD L, A
JP (HL) ; jump to command handler
ccp_cmd_addr:
DW cmd_dir
DW cmd_erase
DW cmd_type
DW cmd_save
DW cmd_ren
DW cmd_user
DW ccp_entry
; ---------------------------------------------------
; di+halt if serial number validation failed
; ---------------------------------------------------
cpp_halt:
LD HL, 0x76f3
LD (ccp_ram_ent), HL
LD HL, ccp_ram_ent
JP (HL)
ccp_type_rd_err:
LD BC, .msg_read_error ; BC -> "READ ERROR"
JP ccp_out_crlf_msg
.msg_read_error:
DB "READ ERROR", 0
; ---------------------------------------------------
; Out message 'NO FILE'
; ---------------------------------------------------
ccp_out_no_file:
LD BC, .msg_no_file ; BC -> "NO FILE"
JP ccp_out_crlf_msg
.msg_no_file:
DB "NO FILE", 0
; ---------------------------------------------------
; Decode a command in form: "A>filename number"
; Out: A - number
; ---------------------------------------------------
ccp_decode_num:
CALL ccp_cv_first_to_fcb
LD A, (ccp_chg_drive)
OR A
JP NZ, print_syn_err ; error if drive letter specified
LD HL, ccp_current_fcb_fn
LD BC, 11 ; b=0, c=11 (filename len)
; decode number
ccp_dff_nxt_num:
LD A, (HL)
CP ASCII_SP
JP Z, ccp_dff_num_fin ; space - end of number
; check for digit
INC HL
SUB '0'
CP 10
JP NC, print_syn_err ; not a digit
LD D, A ; d = number
LD A, B
AND 0xe0 ; check B (sum) overflow
JP NZ, print_syn_err
; A=B*10
LD A, B
RLCA
RLCA
RLCA ; *8
ADD A, B ; *9
JP C, print_syn_err ; error if overflow
ADD A, B ; * 10
JP C, print_syn_err ; error if overflow
; B = B + B*10
ADD A, D
JP C, print_syn_err ; error if overflow
LD B, A
; to next number
DEC C
JP NZ, ccp_dff_nxt_num
RET
ccp_dff_num_fin:
LD A, (HL)
CP ASCII_SP
JP NZ, print_syn_err ; will be space after number
INC HL
DEC C
JP NZ, ccp_dff_num_fin
LD A, B
RET
; --------------------------------------------------
; Move 3 bytes from [HL] to [DE]
; (Used only to move file extension)
; --------------------------------------------------
ccp_mv_hlde_3:
LD B, 3
; --------------------------------------------------
; Move B bytes from [HL] to [DE]
; --------------------------------------------------
ccp_mv_hlde_b:
LD A, (HL)
LD (DE), A
INC HL
INC DE
DEC B
JP NZ, ccp_mv_hlde_b
RET
; --------------------------------------------------
; Return byte from address dma_buffer[A+C]
; Out: A - byte at dma_buffer[A+C]
; HL - dma_buffer+A+C
; --------------------------------------------------
get_std_buff_ac:
LD HL, dma_buffer
ADD A, C
CALL sum_hl_a
LD A, (HL)
RET
; --------------------------------------------------
; Check drive change and select if needed
; Reset drive byte in fcb to 0 (use default)
; --------------------------------------------------
ccp_drive_sel:
XOR A
LD (ccp_current_fcb), A
LD A, (ccp_chg_drive)
OR A
RET Z ; no need to change cur drive
DEC A
LD HL, ccp_cur_drive
CP (HL)
RET Z ; current and new drive is same
JP ccp_bdos_drv_set ; change
; --------------------------------------------------
; Restore previous drive if changed during operation
; --------------------------------------------------
ccp_restor_drv:
LD A, (ccp_chg_drive)
OR A
RET Z ; not changed
DEC A
LD HL, ccp_cur_drive ; chk cur drive
CP (HL)
RET Z ; new and previous drive is same
LD A, (ccp_cur_drive)
JP ccp_bdos_drv_set ; restore to previous drive
; --------------------------------------------------
; Handle user DIR command
; --------------------------------------------------
cmd_dir:
CALL ccp_cv_first_to_fcb
CALL ccp_drive_sel
; check filemask specified
LD HL, ccp_current_fcb_fn
LD A, (HL)
CP ASCII_SP
JP NZ, .dir_fmask ; yes specified
; fill with wildcard symbol
LD B, 11
.fill_wc:
LD (HL), '?'
INC HL
DEC B
JP NZ, .fill_wc
.dir_fmask:
LD E, 0x0 ; cursor at 0
PUSH DE
; find first file
CALL ccp_find_first
CALL Z, ccp_out_no_file ; no file found
.dir_f_next:
JP Z, .dir_f_no_more
LD A, (ccp_bdos_result_code)
; find filename pos in direntry
; a = a * 32
RRCA
RRCA
RRCA
AND 0x60
LD C, A
LD A, 0x0a
; get std_buff[10+pos*8]
CALL get_std_buff_ac
RLA ; CF<-[7:0]<-CF
JP C, .dir_dont_lst ; don't display sys files
POP DE
LD A, E ; a = cursor
INC E
PUSH DE ; cursor++
AND 0x3
PUSH AF
JP NZ, .dir_no_eol
; eol, print new line
CALL ccp_out_crlf
; print A:
PUSH BC
CALL ccp_bdos_drv_get
POP BC
ADD A, 'A'
CALL ccp_putc
LD A, ':'
CALL ccp_putc
JP .dir_out_sp
.dir_no_eol:
; add space between filenames
CALL ccp_out_space
LD A, ':'
CALL ccp_putc
.dir_out_sp:
CALL ccp_out_space
LD B, 0x1
.dir_get_one:
LD A, B
CALL get_std_buff_ac
AND 0x7f ; mask status bit
CP ASCII_SP ; name end?
JP NZ, .no_name_end
; at end of file name
POP AF
PUSH AF
CP 0x3
JP NZ, .dir_end_ext
LD A, 0x9
CALL get_std_buff_ac ; chk ext
AND 0x7f ; 7bit
CP ASCII_SP
JP Z, .dir_skp_sp ; do not print space
.dir_end_ext:
LD A, ASCII_SP
.no_name_end:
CALL ccp_putc
INC B
LD A, B
CP 12
JP NC, .dir_skp_sp ; print until end of file ext
CP 9
JP NZ, .dir_get_one ; start of file ext?
CALL ccp_out_space ; print sep space
JP .dir_get_one
.dir_skp_sp:
POP AF
.dir_dont_lst:
; stop if key pressed
CALL ccp_getkey_no_wait
JP NZ, .dir_f_no_more
; find next directory entry
CALL ccp_bdos_find_next
JP .dir_f_next
; no more to print
.dir_f_no_more:
POP DE
JP ccp_cmdline_back
; --------------------------------------------------
; Handle user ERA command
; --------------------------------------------------
cmd_erase:
CALL ccp_cv_first_to_fcb
; check for *.*
CP 11
JP NZ, .era_no_wc
; confirm erase all
LD BC, msg_all_yn ;= "ALL (Y/N)?"
CALL ccp_out_crlf_msg
CALL ccp_get_inp
LD HL, ccp_inbuff+1
; check user input
DEC (HL)
JP NZ, ccp_get_command
INC HL
LD A, (HL) ; user input, first letter
CP 'Y'
JP NZ, ccp_get_command ; return in not exactly 'Y'
INC HL
LD (ccp_inp_line_addr), HL
.era_no_wc:
CALL ccp_drive_sel ; select drive
LD DE, ccp_current_fcb ; specify current fcb
CALL ccp_bdos_era_file ; and delete file
INC A
CALL Z, ccp_out_no_file
JP ccp_cmdline_back ; go back to command line
msg_all_yn:
DB "ALL (Y/N)?", 0
; --------------------------------------------------
; Handle user TYPE command
; --------------------------------------------------
cmd_type:
CALL ccp_cv_first_to_fcb
JP NZ, print_syn_err ; error if wildcard
; select drive and open
CALL ccp_drive_sel
CALL ccp_open_cur_fcb
JP Z, .not_found ; cant open file
CALL ccp_out_crlf
LD HL, ccp_bytes_ctr
LD (HL), 0xff ; 255>128 for read first sector
.cont_or_read:
LD HL, ccp_bytes_ctr
LD A, (HL)
CP 128
JP C, .out_next_char
; read 128 bytes
PUSH HL
CALL ccp_read_f_fcb
POP HL
JP NZ, .read_no_ok
; clear counter
XOR A
LD (HL), A
.out_next_char:
; get next byte from buffer
INC (HL)
LD HL, dma_buffer
; calc offset
CALL sum_hl_a
LD A, (HL)
CP ASCII_SUB ; Ctrl+Z end of text file
JP Z, ccp_cmdline_back ; yes, back to cmd line
CALL ccp_print ; print char to output device
; interrupt if key pressed
CALL ccp_getkey_no_wait
JP NZ, ccp_cmdline_back
;
JP .cont_or_read
; non zero result from f_read
.read_no_ok:
DEC A
JP Z, ccp_cmdline_back ; A=1 - EOF, return to cmd line
CALL ccp_type_rd_err ; else read error
.not_found:
CALL ccp_restor_drv
JP print_syn_err
; --------------------------------------------------
; Handle user SAVE command
; --------------------------------------------------
cmd_save:
CALL ccp_decode_num ; get num of pages
PUSH AF ; and store
CALL ccp_cv_first_to_fcb ; conv filename to fcb
JP NZ, print_syn_err ; error if wildcard
; delete specified file
CALL ccp_drive_sel
LD DE, ccp_current_fcb
PUSH DE
CALL ccp_bdos_era_file
POP DE
; create specified file
CALL ccp_bdos_make_f
JP Z, ccp_no_space ; 0xff+1 if error
XOR A
LD (ccp_current_fcb_cr), A ; curr record = 0
POP AF ; a = num pages
LD L, A
LD H, 0
ADD HL, HL ; HL = A * 2 - number of sectors
LD DE, tpa_start
.write_next:
; all sectors written?
LD A, H
OR L
JP Z, ccp_close_f_cur ; no more sectors to write
DEC HL
PUSH HL
; set buffer address to memory to write
LD HL, 128
ADD HL, DE
PUSH HL
CALL ccp_bdos_dma_set
; and write sector
LD DE, ccp_current_fcb
CALL ccp_bdos_f_write
POP DE
POP HL
JP NZ, ccp_no_space ; check for no space left
JP .write_next
; Close current file
ccp_close_f_cur:
LD DE, ccp_current_fcb
CALL ccp_bdos_close_f
INC A
JP NZ, rest_buf_ret_cmd
; --------------------------------------------------
; Out error message about no space left
; --------------------------------------------------
ccp_no_space:
LD BC, msg_no_space ; BC -> "NO SPACE"
CALL ccp_out_crlf_msg
rest_buf_ret_cmd:
CALL cpp_set_disk_buff_addr
JP ccp_cmdline_back
msg_no_space:
DB "NO SPACE", 0
; --------------------------------------------------
; Handle user REN command
; --------------------------------------------------
cmd_ren:
CALL ccp_cv_first_to_fcb ; get first file name
JP NZ, print_syn_err ; error if wildcard
LD A, (ccp_chg_drive) ; remember drive change flag
PUSH AF
; check file already exists
CALL ccp_drive_sel
CALL ccp_find_first
JP NZ, .file_exists
; move filename to "second slot"
LD HL, ccp_current_fcb
LD DE, ccp_current_fcb_fn+15
LD B, 16
CALL ccp_mv_hlde_b
;
LD HL, (ccp_inp_line_addr) ; restore cmd line pointer
EX DE, HL
CALL ccp_find_no_space ; skip spaces between parameters
CP '='
JP Z, .do_rename
CP '_'
JP NZ, .rename_err
.do_rename:
EX DE, HL
INC HL ; skip sep
LD (ccp_inp_line_addr), HL ; -> second param
CALL ccp_cv_first_to_fcb ; get second name
JP NZ, .rename_err ; error if wildcard
; if drive specified it will be same as previous
POP AF
LD B, A
LD HL, ccp_chg_drive
LD A, (HL)
OR A
JP Z, .same_drive ; ok, it is same
CP B
LD (HL), B ; restore first drive
JP NZ, .rename_err
.same_drive:
LD (HL), B
; check for seacond file not exists
XOR A
LD (ccp_current_fcb), A
CALL ccp_find_first
JP Z, .second_exists
; calll bdos to rename
LD DE, ccp_current_fcb
CALL ccp_bdos_rename_f
JP ccp_cmdline_back
.second_exists:
CALL ccp_out_no_file
JP ccp_cmdline_back
.rename_err:
CALL ccp_restor_drv
JP print_syn_err
.file_exists:
LD BC, .msg_file_exists ; BC -> "FILE EXISTS"
CALL ccp_out_crlf_msg
JP ccp_cmdline_back
.msg_file_exists:
DB "FILE EXISTS", 0
; --------------------------------------------------
; Handle user USER command
; --------------------------------------------------
cmd_user:
CALL ccp_decode_num ; get user number
; user will be 0..15
CP 16
JP NC, print_syn_err ; >15 - error
LD E, A ; save in E
; check for other parameters
LD A, (ccp_current_fcb_fn)
CP ASCII_SP
JP Z, print_syn_err ; error if other parameters specified
; call bdos to set current user
CALL ccp_bdos_set_user
JP ccp_cmdline_back1
ccp_unk_cmd:
CALL ccp_verify_pattern ; check if system valid
; check for file to execute specified
LD A, (ccp_current_fcb_fn)
CP ASCII_SP
JP NZ, .exec_file
; drive change?
LD A, (ccp_chg_drive)
OR A
JP Z, ccp_cmdline_back1 ; no, return to cmd line
; change drive
DEC A
LD (ccp_cur_drive), A
CALL ccp_reset_cur_drv
CALL ccp_bdos_drv_set
JP ccp_cmdline_back1
.exec_file:
; check file extension
LD DE, ccp_current_fcb_ft
LD A, (DE)
CP ASCII_SP
JP NZ, print_syn_err
PUSH DE
; select specified drive
CALL ccp_drive_sel
POP DE
; set file ext to 'COM'
LD HL, msg_com ; HL -> 'COM'
CALL ccp_mv_hlde_3
CALL ccp_open_cur_fcb
JP Z, cant_open_exe
LD HL, tpa_start ; load to start of TPA 0x100
.read_next_sec:
PUSH HL ; store start pointer
EX DE, HL
CALL ccp_bdos_dma_set
LD DE, ccp_current_fcb
CALL ccp_bdos_read_f
JP NZ, .read_no_ok ; check for read error
; shift start pointer for sector size
POP HL
LD DE, 128
ADD HL, DE
; check for enough space in RAM
LD DE, ccp_ram_ent
LD A, L
SUB E
LD A, H
SBC A, D
JP NC, ccp_bad_load
JP .read_next_sec
.read_no_ok:
POP HL
DEC A
JP NZ, ccp_bad_load ; it is not EOF, is error
; ok, EOF
CALL ccp_restor_drv ; get first filename
CALL ccp_cv_first_to_fcb
LD HL, ccp_chg_drive ; hl -> buff[16]
PUSH HL
LD A, (HL)
LD (ccp_current_fcb), A ; set drive letter in current fcb
LD A, 16
CALL ccp_cv_fcb_filename ; replace wildcards
POP HL
; set drive for second file in reserved fcb area
LD A, (HL)
LD (ccp_current_fcb_al), A
; clear record count
XOR A
LD (ccp_current_fcb_cr), A
; Move current to default FCB
LD DE, fcb1
LD HL, ccp_current_fcb
LD B, 33
CALL ccp_mv_hlde_b
; move remainder of cmd line to 0x0080
LD HL, ccp_inp_line
.skip_nosp:
LD A, (HL)
OR A
JP Z, .z_or_sp
CP ASCII_SP
JP Z, .z_or_sp
INC HL
JP .skip_nosp
.z_or_sp:
LD B, 0 ; len of cmd line for program = 0
LD DE, p_cmd_line ; destination address for cmd line
.copy_cmd_line:
LD A, (HL)
LD (DE), A
OR A
JP Z, .stor_len
INC B
INC HL
INC DE
JP .copy_cmd_line
.stor_len:
LD A, B
LD (p_cmd_line_len), A
; next line
CALL ccp_out_crlf
; set buffer to cmd line
CALL cpp_set_disk_buff_addr
; set drive
CALL ccp_set_cur_drv
; and call loaded program
CALL tpa_start
; restore stack first
LD SP, ccp_stack
; restore current drive
CALL ccp_reset_cur_drv
CALL ccp_bdos_drv_set
; return back to command line mode
JP ccp_get_command
cant_open_exe:
CALL ccp_restor_drv
JP print_syn_err
ccp_bad_load:
LD BC, .msg_bad_load ; BC -> "BAD LOAD"
CALL ccp_out_crlf_msg
JP ccp_cmdline_back
.msg_bad_load:
DB "BAD LOAD", 0
msg_com:
DB "COM"
; --------------------------------------------------
; Return back to command line
; --------------------------------------------------
ccp_cmdline_back:
CALL ccp_restor_drv
ccp_cmdline_back1:
CALL ccp_cv_first_to_fcb
LD A, (ccp_current_fcb_fn)
SUB 0x20
LD HL, ccp_chg_drive
OR (HL)
JP NZ, print_syn_err
JP ccp_get_command
DW 0h, 0h, 0h, 0h, 0h, 0h, 0h, 0h
ccp_stack EQU $
ccp_batch:
DB 0h
ccp_batch_fcb:
DB 0h ; drive code, 0 - default
DB "$$$ SUB" ; filename
DB 0h ; extent
DB 0h ; S1
ccp_batch_fcb_s2:
DB 0h ; S2 Extent [6:0] bits and [7] write flag
ccp_batch_fcb_rc:
DB 0h ; sectors count
ccp_batch_fcb_al:
DS 16, 0 ; reserved by CPM use only
ccp_batch_fcb_cr:
DB 0h ; current sector to read/write
ccp_current_fcb:
DB 0h
ccp_current_fcb_fn:
DS 8, 0
ccp_current_fcb_ft:
DS 3, 0
ccp_current_fcb_ex:
DB 0h ; extent
DB 0h ; s1
DB 0h ; s2
DB 0h ; sectors count
ccp_current_fcb_al:
DS 16, 0 ; reserved by CPM use only
ccp_current_fcb_cr:
DB 0h ; current sector to read/write
ccp_bdos_result_code:
DB 0h
ccp_cur_drive:
DB 0h
; change drive flag, 0 - no change
ccp_chg_drive:
DB 0
ccp_bytes_ctr:
DW 0
; reserved
DS 13, 0
; head of BDOS, copyed at start form ROM to RAM
; LD SP,HL
; LD D,0
; NOP
; NOP
; LD L,E
; JP BDOS.bdos_entrance
bdos_enter_jump EQU $+6
; -------------------------------------------------------
; Filler to align blocks in ROM
; -------------------------------------------------------
LAST EQU $
CODE_SIZE EQU LAST-0xB200
;FILL_SIZE EQU 0x500-CODE_SIZE
DISPLAY "| CCP_RAM\t| ",/H,ccp_ram_ent," | ",/H,CODE_SIZE," | \t |"
ENDMODULE
IFNDEF BUILD_ROM
OUTEND
ENDIF