Ocean-240.2-Emulator/examples/hello/cpm/ccp_rom.asm
2026-04-01 14:57:08 +03:00

883 lines
18 KiB
NASM

; =======================================================
; Ocean-240.2
; CPM CPP, ROM PART
; AT 0xDB00
;
; Disassembled by Romych 2025-09-09
; =======================================================
INCLUDE "equates.inc"
INCLUDE "ram.inc"
IFNDEF BUILD_ROM
OUTPUT ccp_rom.bin
ENDIF
MODULE CCP_ROM
ORG 0xDB00
@ccp_entry:
LD HL, 0x0 ; prevent stack overflow
ADD HL, SP
LD (CPM_VARS.saved_stack_ptr), HL
LD SP, CPM_VARS.ccp_safe_stack
CALL get_cmd_index
LD HL, ccp_commands ;= DB6Ch
LD E, A
LD D, 0x0
ADD HL, DE
ADD HL, DE
LD A, (HL)
INC HL
LD H, (HL)
LD L, A
JP (HL) ; jump to command
ccp_commands_str:
DB "SDIR READ WRITE"
; -------------------------------------------------------
; Search user command position in available commands list
; -------------------------------------------------------
get_cmd_index:
LD HL, ccp_commands_str ; -> 'DIR'
LD C, 0x0
.cmd_next:
LD A, C
CP CCP_COMMAND_COUNT
RET NC
LD DE, CCP_RAM.ccp_current_fcb_fn
LD B, CCP_COMMAND_SIZE
.cmp_nxt:
LD A, (DE)
CP (HL) ; -> 'DIR'
JP NZ, .no_eq
INC DE
INC HL
DEC B
JP NZ, .cmp_nxt
LD A, (DE)
CP ASCII_SP
JP NZ, .inc_next
LD A, C
RET
.no_eq:
INC HL
DEC B
JP NZ, .no_eq
.inc_next:
INC C
JP .cmd_next
; --------------------------------------------------
; Command handlers ref table
; --------------------------------------------------
ccp_commands:
DW ccp_dir
DW ccp_read
DW ccp_write
DW ccp_ret ; r8
; DW ccp_exit1 ; r8
ccp_ret:
LD HL, (CPM_VARS.saved_stack_ptr)
LD SP, HL
JP CCP_RAM.ccp_unk_cmd
;ccp_exit1:
; JP MON.mon_hexb
; --------------------------------------------------
; DIR [filemask] command handler
; --------------------------------------------------
ccp_dir:
CALL CCP_RAM.ccp_cv_first_to_fcb
CALL CCP_RAM.ccp_drive_sel
; chech some filemask specified in command line
LD HL, CCP_RAM.ccp_current_fcb_fn
LD A, (HL)
CP ' '
JP NZ, .has_par
; no filemask, fill with wildcard '?'
LD B, 11
.fill_wildcard:
LD (HL), '?'
INC HL
DEC B
JP NZ, .fill_wildcard
; find file by specified mask
.has_par:
CALL CCP_RAM.ccp_find_first
JP NZ, .f_found
; no files found, print and exit
CALL CCP_RAM.ccp_out_no_file
JP CCP_RAM.ccp_cmdline_back
.f_found:
CALL CCP_RAM.ccp_out_crlf
LD HL, 0x0
LD (CPM_VARS.tmp_dir_total), HL
LD E, 0
.do_next_direntry:
PUSH DE
CALL CCP_RAM.ccp_find_first
POP DE
PUSH DE
; Find file with e number
.find_file_e:
DEC E
JP M, .file_out_next
PUSH DE
CALL CCP_RAM.ccp_bdos_find_next
POP DE
JP Z, .file_e_found
JP .find_file_e
.file_out_next:
LD A, (CCP_RAM.ccp_bdos_result_code)
; calc address of DIR entry in DMA buffer
; A[6:5] = A[1:0] = 32*A
RRCA ; [C] -> [7:0] -> [C]
RRCA ;
RRCA ;
AND 01100000b ; mask
LD C, A
PUSH BC
CALL CCP_RAM.ccp_out_crlf ; start new line
CALL CCP_RAM.ccp_bdos_drv_get
INC A
LD (dma_buffer), A ; disk
POP BC
LD B, 0x0
LD HL, dma_buffer+FCB_FN ; filename
LD E, L
LD D, H
ADD HL, BC
; copy filename to tmp FCB and out to screen
LD B, 0x1
.copy_next:
LD A, (HL)
LD (DE), A
LD C, A
CALL BIOS.conout_f
INC HL
INC DE
INC B
LD A, B
CP FN_LEN ; >12 end of name
JP C, .copy_next
.zero_up_36:
XOR A
LD (DE), A ; zero at end
INC B
LD A, B
CP 36
JP C, .zero_up_36
; calc file size for current entry
LD DE, dma_buffer
CALL cpp_bdos_f_size
LD HL, (fcb_ra_record_num) ; file size in blocks
; get disk blk size
LD A, (CPM_VARS.bdos_curdsk)
OR A
JP NZ, .no_dsk_a0
LD B, 3 ; for A - blk=3
JP .dsk_a0
.no_dsk_a0:
LD B, 4 ; for other disks - blk=4
.dsk_a0:
LD C, L
; convert 128b OS block to disk blocks
.mul_to_dsk_blk:
XOR A
LD A, H
RRA
LD H, A
LD A, L
RRA
LD L, A
DEC B
JP NZ, .mul_to_dsk_blk
; round up
LD A, (CPM_VARS.bdos_curdsk)
OR A
JP NZ, .no_dsk_a1
LD A, 00000111b ; for A - ~(~0 << 3)
JP .ds_skip1
.no_dsk_a1:
LD A, 00001111b ; for other dsk - ~(~0 << 4)
.ds_skip1:
AND C
JP Z, .cvt_blk_kb
INC HL
; Convert blocks to kilobytes (A-1k B-2k)
.cvt_blk_kb:
LD A, (CPM_VARS.bdos_curdsk)
OR A
JP Z, .ds_skip2
ADD HL, HL ; 2k
; add file size to total dir size
.ds_skip2:
EX DE, HL
LD HL, (CPM_VARS.tmp_dir_total)
ADD HL, DE
LD (CPM_VARS.tmp_dir_total), HL
; display size in K
LD C, ' '
CALL BIOS.conout_f
CALL BIOS.conout_f
CALL ccp_cout_num
LD C, 'K'
CALL BIOS.conout_f
CALL CCP_RAM.ccp_getkey_no_wait
JP NZ, CCP_RAM.ccp_cmdline_back
POP DE
INC E
JP .do_next_direntry
.file_e_found:
POP DE
LD HL, msg_free_space ;= "\r\nFREE SPACE "
; Out: FREE SPACE
CALL ccp_out_str_z
LD A, (CPM_VARS.bdos_curdsk)
OR A
JP NZ, .no_ram_dsk
LD HL, (BIOS.disk_a_size)
JP .calc_remanis_ds
.no_ram_dsk:
DEC A
LD HL, (BIOS.disk_b_size)
JP Z, .calc_remanis_ds
LD HL, (BIOS.disk_c_size)
LD A, (disk_sw_trk)
OR A
JP NZ, .calc_remanis_ds
LD A, H
CP 1
JP Z, .d720
LD HL, 360
JP .calc_remanis_ds
.d720:
LD HL, 720
; Disk size - Dir size = Free
.calc_remanis_ds:
EX DE, HL
LD HL, (CPM_VARS.tmp_dir_total)
LD A, E
SUB L
LD E, A
LD A, D
SBC A, H
LD D, A
CALL ccp_cout_num
LD C, 'K'
CALL BIOS.conout_f
CALL CCP_RAM.ccp_out_crlf
JP CCP_RAM.ccp_cmdline_back
msg_free_space:
DB "\r\nFREE SPACE ",0
ccp_cout_num:
LD A, D
AND 11100000b
JP Z, .less_224
LD C, '*'
CALL BIOS.conout_f
CALL BIOS.conout_f
CALL BIOS.conout_f
CALL BIOS.conout_f
RET
.less_224:
LD HL, 0x0
; copy number to BC
LD B, D
LD C, E
LD DE, 0x1
LD A, 13
.bc_rra:
PUSH AF
PUSH HL
; BC >> 1
LD A, B
RRA
LD B, A
LD A, C
RRA
LD C, A
JP NC, .bc_rra_ex
POP HL
CALL cpp_daa16
PUSH HL
.bc_rra_ex:
LD L, E
LD H, D
CALL cpp_daa16
EX DE, HL
POP HL
POP AF
DEC A
JP NZ, .bc_rra
LD D, 4
LD B, 0
.next_d:
LD E, 4
.next_e:
LD A, L
RLA
LD L, A
LD A, H
RLA
LD H, A
LD A, C
RLA
LD C, A
DEC E
JP NZ, .next_e
LD A, C
AND 0xf
ADD A, '0'
CP '0'
JP NZ, .no_zero
DEC B
INC B
JP NZ, .b_no_one
LD A, D
DEC A
JP Z, .d_one
LD A, ' '
JP .b_no_one
.d_one:
LD A, '0'
.no_zero:
LD B, 0x1
.b_no_one:
LD C, A
CALL BIOS.conout_f
DEC D
JP NZ, .next_d
RET
; -------------------------------------------------------
; ADD with correction HL=HL+DE
; -------------------------------------------------------
cpp_daa16:
LD A, L
ADD A, E
DAA
LD L, A
LD A, H
ADC A, D
DAA
LD H, A
RET
; -------------------------------------------------------
; Call BDOS function 35 (F_SIZE) - Compute file size
; -------------------------------------------------------
cpp_bdos_f_size:
LD C, F_SIZE
JP jp_bdos_enter
; -------------------------------------------------------
; Read Intel HEX data from serial port
; -------------------------------------------------------
ccp_read:
LD DE, msg_read_hex
CALL out_dollar_str
LD HL, 0x0
LD (CCP_RAM.hex_length), HL
; Wait for start of Intel HEX line
.wait_colon:
CALL MON.mon_serial_in
CP ':'
JP NZ, .wait_colon
; Init checksum
XOR A
LD D, A
CALL ser_read_hexb ; read byte_count
JP Z, .end_of_file
LD E, A
CALL ser_read_hexb ; read address hi
LD H, A
CALL ser_read_hexb ; read address lo
LD L, A ; HL - dst address
CALL ser_read_hexb ; read rec type
; calculate length += byte_count
PUSH HL
LD HL, (CCP_RAM.hex_length)
LD A, L
ADD A, E
LD L, A
LD A, H
ADC A, 0
LD H, A
LD (CCP_RAM.hex_length), HL
POP HL
LD C, E
; receive next E=byte_count bytes
.receive_rec:
CALL ser_read_hexb
LD (HL), A
INC HL
DEC E
JP NZ, .receive_rec
CALL ser_read_hexb ; receive checksum
JP NZ, .load_error ; jump if error
JP .wait_colon ; jump to wait next line
.end_of_file:
; read tail 4 bytes: 00 00 01 ff
CALL ser_read_hexb
CALL ser_read_hexb
CALL ser_read_hexb
CALL ser_read_hexb
JP Z, .load_complete
.load_error:
LD DE, .msg_error
JP out_dollar_str
.load_complete:
; Out message with length of received file
LD HL, (CCP_RAM.hex_length)
LD A, H
CALL MON.mon_hexb
LD A, L
CALL MON.mon_hexb
LD DE, .msg_bytes
CALL out_dollar_str
; Calculate number of pages
LD HL, (CCP_RAM.hex_length)
LD A, L
ADD A, 0xff
LD A, H
ADC A, 0x0
RLA
LD (CCP_RAM.hex_sectors), A
; Out message with number of pages
CALL MON.mon_hexb
LD DE, .msg_pages
CALL out_dollar_str
; Check for file name specified in cmd line
CALL CCP_RAM.ccp_cv_first_to_fcb
CALL CCP_RAM.ccp_drive_sel
LD A, (CCP_RAM.ccp_current_fcb_fn)
CP ASCII_SP
JP Z, .warm_boot
; Create file
LD DE, CCP_RAM.ccp_current_fcb
LD C, F_MAKE
CALL jp_bdos_enter
INC A
JP Z, .load_error
LD HL, tpa_start
LD (CCP_RAM.hex_buff), HL
.wr_sector:
; set source buffer address
LD HL, (CCP_RAM.hex_buff)
EX DE, HL
LD C, F_DMAOFF
CALL jp_bdos_enter
; write source buffer to disk
LD DE, CCP_RAM.ccp_current_fcb
LD C, F_WRITE
CALL jp_bdos_enter
; check errors
OR A
JP NZ, .load_error
; rewind forward to next sector
LD HL, (CCP_RAM.hex_buff)
LD DE, 128 ; sector size
ADD HL, DE
LD (CCP_RAM.hex_buff), HL
; decrement sector count
LD A, (CCP_RAM.hex_sectors)
DEC A
LD (CCP_RAM.hex_sectors), A
JP NZ, .wr_sector ; jump if remains sectors
; close file
LD DE, CCP_RAM.ccp_current_fcb
LD C, F_CLOSE
CALL jp_bdos_enter
; check errors
CP 0xff
JP Z, .load_error
.warm_boot:
LD C, P_TERMCPM
JP jp_bdos_enter
.msg_bytes:
DB "h bytes ($"
.msg_pages:
DB " pages)\n\r$"
.msg_error:
DB "error!\n\r$"
; ---------------------------------------------------
; Read next two symbols from serial and convert to
; byte
; Out: A - byte
; CF set if error
; ---------------------------------------------------
ser_read_hexb:
PUSH BC
CALL MON.mon_serial_in
CALL hex_to_nibble
RLCA
RLCA
RLCA
RLCA
LD C, A
CALL MON.mon_serial_in
CALL hex_to_nibble
OR C
LD C, A
ADD A, D
LD D, A
LD A, C
POP BC
RET
; ---------------------------------------------------
; Convert hex symbol to byte
; Inp: A - '0'..'F'
; Out: A - 0..15
; CF set if error
; ---------------------------------------------------
hex_to_nibble:
SUB '0' ; < '0' - error
RET C
ADD A, 233 ; F -> 255
RET C ; > F - error
ADD A, 6
JP P, .l1
ADD A, 7
RET C
.l1:
ADD A, 10
OR A
RET
; ---------------------------------------------------
; Out $ ended string
; Inp: DE -> string$
; ---------------------------------------------------
out_dollar_str:
LD C, C_WRITESTR
JP jp_bdos_enter
msg_read_hex:
DB "\n\rRead HEX from RS232... $"
filler1:
DS 62, 0
; -------------------------------------------------------
; Out zerro ended string
; In: HL -> strZ
; -------------------------------------------------------
ccp_out_str_z:
LD A, (HL)
OR A
RET Z
LD C, A
CALL BIOS.conout_f
INC HL
JP ccp_out_str_z
; -------------------------------------------------------
; Delete file and out No Space message
; -------------------------------------------------------
ccp_del_f_no_space:
LD DE, CCP_RAM.ccp_current_fcb
CALL CCP_RAM.ccp_bdos_era_file
JP CCP_RAM.ccp_no_space
; -------------------------------------------------------
; Read current file next block
; Out: A=0 - Ok, 0xFF - HW Error;
; -------------------------------------------------------
cpp_read_f_blk:
LD DE, CPM_VARS.ccp_fcb ; FCB here
JP CCP_RAM.ccp_bdos_read_f
ccp_write:
CALL CCP_RAM.ccp_cv_first_to_fcb
CALL CCP_RAM.ccp_drive_sel
LD HL, CCP_RAM.ccp_current_fcb_fn
LD A, (HL)
CP ' '
JP NZ, .find_f
LD B, 11
.fill_with_wc:
LD (HL), '?'
INC HL
DEC B
JP NZ, .fill_with_wc
.find_f:
CALL CCP_RAM.ccp_find_first
JP NZ, .found_f
CALL CCP_RAM.ccp_out_no_file
JP CCP_RAM.ccp_cmdline_back
.found_f:
LD E, 0 ; file counter
.do_next_f1:
PUSH DE
CALL CCP_RAM.ccp_find_first
POP DE
PUSH DE
.do_next_f2:
DEC E
JP M, .do_file
PUSH DE
CALL CCP_RAM.ccp_bdos_find_next
POP DE
JP Z, .no_more_f
JP .do_next_f2
.do_file:
POP BC
PUSH BC
LD A, C
OR A
JP Z, .calc_addr
LD DE, 1200
; Delay with key interrupt check
.delay_1:
XOR A
.delay_2:
DEC A
JP NZ, .delay_2
PUSH DE
CALL CCP_RAM.ccp_getkey_no_wait
POP DE
JP NZ, CCP_RAM.ccp_cmdline_back
DEC DE
LD A, D
OR E
JP NZ, .delay_1
.calc_addr:
LD A, (CCP_RAM.ccp_bdos_result_code)
; A=0-3 - for Ok, calc address of DIR entry in DMA buffer
RRCA
RRCA
RRCA
AND 01100000b
LD C, A
PUSH BC
CALL CCP_RAM.ccp_out_crlf
CALL CCP_RAM.ccp_bdos_drv_get
INC A
LD (CPM_VARS.ccp_fcb_dr), A ; set drive number
POP BC
LD B, 0x0
LD HL, dma_buffer+1
ADD HL, BC
LD DE, CPM_VARS.ccp_fcb_fn
LD B, 0x1
.copy_fn:
LD A, (HL)
LD (DE), A
INC HL
INC DE
INC B
LD A, B
CP 12
JP C, .copy_fn
.fillz_fn:
XOR A
LD (DE), A
INC DE
INC B
LD A, B
CP 36
JP C, .fillz_fn
LD HL, CPM_VARS.ccp_fcb_fn
CALL ccp_out_str_z
LD HL, dma_buffer
; Empty first 128 bytes of DMA buffer
LD B, 128
.clear_buf:
LD (HL), 0x0
INC HL
DEC B
JP NZ, .clear_buf
; Copy file name at buffer start
LD HL, dma_buffer
LD DE, CPM_VARS.ccp_fcb_fn
LD B, 8
.find_sp:
LD A, (DE)
CP ' '
JP Z, .sp_rep_dot ; ' ' -> '.'
LD (HL), A
INC HL
INC DE
JP .find_sp
.sp_rep_dot:
LD (HL), '.'
INC HL
CALL CCP_RAM.ccp_find_no_space
.cont_copy_fn:
LD A, (DE)
LD (HL), A
OR A
JP Z, .end_copy
INC HL
INC DE
JP .cont_copy_fn
.end_copy:
LD DE, CPM_VARS.ccp_fcb
CALL CCP_RAM.ccp_bdos_open_f
LD DE, 0x8000 ; Block ID
LD HL, dma_buffer
CALL cpp_pause_tape_wr_blk
CALL cpp_pause_tape_wr_blk
LD DE, 0x1
; Read file block and write to Tape
.read_f_write_t:
PUSH DE
CALL cpp_read_f_blk
; a=0xff if error; a=1 - EOF
DEC A
JP Z, .eof
LD A, (CPM_VARS.ccp_fcb_cr)
AND 0x7f
CP 0x1
JP NZ, .write_once
; Write block to Tape with ID=0 twice
LD DE, 0x0 ; Block ID=0
LD HL, dma_buffer
CALL cpp_pause_tape_wr_blk
.write_once:
CALL CCP_RAM.ccp_getkey_no_wait
LD HL, dma_buffer
POP DE
JP NZ, CCP_RAM.ccp_cmdline_back
CALL cpp_pause_tape_wr_blk
; Inc Block ID and continue
INC DE
JP .read_f_write_t
.eof:
POP DE
EX DE, HL
LD (dma_buffer), HL
EX DE, HL
; Final block ID=0xFFFF
LD DE, 0xffff
; Write twice
CALL cpp_pause_tape_wr_blk
CALL cpp_pause_tape_wr_blk
POP DE
INC E
JP .do_next_f1
.no_more_f:
POP DE
CALL CCP_RAM.ccp_out_crlf
JP CCP_RAM.ccp_cmdline_back
; -------------------------------------------------------
; Write block to tape after pause
; -------------------------------------------------------
cpp_pause_tape_wr_blk:
LD BC, 3036
.delay:
DEC BC
LD A, B
OR C
JP NZ, .delay
JP BIOS.tape_write_f
DB 0x4c
; -------------------------------------------------------
; Filler to align blocks in ROM
; -------------------------------------------------------
LAST EQU $
CODE_SIZE EQU LAST-0xDB00
FILL_SIZE EQU 0x500-CODE_SIZE
DISPLAY "| CCP_ROM\t| ",/H,ccp_entry," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |"
; Check integrity
ASSERT ccp_dir = 0xdb62
ASSERT ccp_dir.find_file_e = 0xdb97
ASSERT ccp_dir.file_out_next = 0xdba6
ASSERT ccp_dir.file_e_found = 0xdc3e
ASSERT ccp_dir.calc_remanis_ds = 0xdc72
ASSERT ccp_dir.no_ram_dsk = 0xdc52
ASSERT msg_free_space = 0xdc8a
ASSERT ccp_cout_num = 0xdc9a
FILLER
DS FILL_SIZE-1, 0xff
DB 0xaa
ENDMODULE
IFNDEF BUILD_ROM
OUTEND
ENDIF