diff --git a/CPM_v2.2_r8_099942fc/.gitignore b/CPM_v2.2_r8_099942fc/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/CPM_v2.2_r8_099942fc/.vscode/extensions.json b/CPM_v2.2_r8_099942fc/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/CPM_v2.2_r8_099942fc/.vscode/tasks.json b/CPM_v2.2_r8_099942fc/.vscode/tasks.json new file mode 100644 index 0000000..f71ba83 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make CPM (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=cpm.sld", + "--sym=cpm.labels", + "--lstlab=sort", + "--lst=cpm.lst", + "--raw=cpm.obj", + "--fullpath", + "cpm.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/BIN/CPM_REL_8_099942fc.BIN b/CPM_v2.2_r8_099942fc/BIN/CPM_REL_8_099942fc.BIN new file mode 100644 index 0000000..9217b19 Binary files /dev/null and b/CPM_v2.2_r8_099942fc/BIN/CPM_REL_8_099942fc.BIN differ diff --git a/CPM_v2.2_r8_099942fc/README.md b/CPM_v2.2_r8_099942fc/README.md new file mode 100644 index 0000000..61f13f6 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/README.md @@ -0,0 +1,11 @@ +# Ocean-240.2 ROM Sources CP/M (V2.2) REL.8 checksum 099942fc + +Source codes of CP/M (V2.2) REL.8 version for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +* 64k RAM Drive + +## Compile: + sjasmplus --sld=cpm.sld --sym=cpm.labels --raw=cpm.obj --fullpath cpm.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/CPM_v2.2_r8_099942fc/bdos.asm b/CPM_v2.2_r8_099942fc/bdos.asm new file mode 100644 index 0000000..3d21d38 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/bdos.asm @@ -0,0 +1,3041 @@ +; ======================================================= +; 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 + DW bdos_get_con_st ; fn11 + DW bdos_get_version ; fn12 + DW bdos_reset_disks ; fn13 + DW bdos_select_disk ; fn14 + DW bdos_open_file ; fn15 + DW bdos_close_file ; fn16 + DW bdos_search_first ; fn17 + DW bdos_search_next ; fn18 + DW bdos_rm_file ; fn19 + DW bdos_read_file ; fn20 + DW bdos_write_file ; fn21 + DW bdos_make_file ; fn22 + DW bdos_ren_file ; fn23 + DW bdos_get_login_vec ; fn24 + DW bdos_get_cur_drive ; fn25 + DW bdos_set_dma_addr ; fn26 + DW bdos_get_alloc_addr ; fn27 + DW bdos_set_ro ; fn28 + DW bdos_get_wr_protect ; fn29 + DW bdos_set_attr ; fn30 + DW bdos_get_dpb ; fn31 + DW bdos_set_user ; fn32 + DW bdos_rand_read ; fn33 + DW bdos_rand_write ; fn34 + DW bdos_compute_fs ; fn35 + DW bdos_set_random ; fn36 + DW bdos_reset_drives ; fn37 + DW bdos_not_impl ; fn38 (Access Drive) + DW bdos_not_impl ; fn39 (Free Drive) + DW bdos_rand_write_z ; fn40 + +; ------------------------------------------------------- +; 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.dbos_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.dbos_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.dbos_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 + +; ------------------------------------------------------- +; 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 diff --git a/CPM_v2.2_r8_099942fc/bios.asm b/CPM_v2.2_r8_099942fc/bios.asm new file mode 100644 index 0000000..8673ef3 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/bios.asm @@ -0,0 +1,823 @@ +; ======================================================= +; Ocean-240.2 +; +; CP/M BIOS +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "mon_entries.inc" + + IFNDEF BUILD_ROM + OUTPUT bios.bin + ENDIF + + MODULE BIOS + + ORG 0xD600 + +; ------------------------------------------------------- +; BIOS JUMP TABLE +; ------------------------------------------------------- +boot_f: + JP bios_boot +wboot_f: + JP bios_wboot + +; ------------------------------------------------------- +; console status to reg-a +; ------------------------------------------------------- +const_f: + JP MON.non_con_status + +; ------------------------------------------------------- +; console character to reg-a +; ------------------------------------------------------- +conin_f: + JP MON.mon_con_in + +; ------------------------------------------------------- +; console character from c to console out +; ------------------------------------------------------- +conout_f: + JP MON.mon_con_out + +; ------------------------------------------------------- +; list device out +; ------------------------------------------------------- +list_f: + JP MON.mon_char_print + +; ------------------------------------------------------- +; punch device out +; ------------------------------------------------------- +punch_f: + JP MON.mon_serial_out + +; ------------------------------------------------------- +; reader character in to reg-a +; ------------------------------------------------------- +reader_f: + JP MON.mon_serial_in + +; ------------------------------------------------------- +; move to home position, treat as track 00 seek +; ------------------------------------------------------- +home_f: + JP home + +; ------------------------------------------------------- +; select disk given by register c +; ------------------------------------------------------- +seldsk_f: + JP sel_disk +settrk_f: + JP set_trk +setsec_f: + JP set_sec + +; ------------------------------------------------------- +; Set DMA address from BC +; ------------------------------------------------------- +setdma_f: + JP set_dma +read_f: + JP read +write_f: + JP write +listst_f: + JP list_st +sectran_f: + JP sec_tran + +; ------------------------------------------------------- +; Reserved +; ------------------------------------------------------- + JP warm_boot + JP warm_boot + +; ------------------------------------------------------- +; Tape read +; ------------------------------------------------------- +tape_read_f: + JP MON.mon_tape_read + +; ------------------------------------------------------- +; Tape write +; ------------------------------------------------------- +tape_write_f: + JP MON.mon_tape_write + +; ------------------------------------------------------- +; Tape wait block +; ------------------------------------------------------- +tape_wait_f: + JP MON.mon_tape_wait + + JP warm_boot ; r8 + +disk_a_size DW 0x0040 ; 64 disk A size +disk_b_size DW 0x02d0 ; 720 disk B size +disk_c_size DW 0x02d0 ; 720 disk C size +bios_var04 DB 0x50 +bios_var05 DB 0x50 + +; ------------------------------------------------------- +; cold start +; ------------------------------------------------------- +bios_boot: + LD HL, (bdos_ent_addr) ; 0xba06 + LD DE, 0x10000-CCP_RAM.bdos_enter_jump ; 0x45fa + ADD HL, DE ; 0xba06+0x45fa=10000 + LD A, H + OR L + JP Z, bios_signon + + ; >> r8 + LD HL, (bios_var0) ; if bios_var0 = 0xaaaa (initialized) + LD DE, 0x5556 + ADD HL, DE ; 0xaaaa+0x5556=0x10000 if initialized + LD A, H + OR L + JP Z, bios_signon ; if initialized, go to logon, skip init + ; << + + ; Init DMA buffer + LD HL, dma_buffer ; 0x8000 + LD B, DMA_BUFF_SIZE ; 0x80 +.init_dma_buff: + LD (HL), FILE_DELETED ; 0xE5 + INC HL + DEC B + JP NZ, .init_dma_buff + + ; Init RAM disk + LD HL, dma_buffer + LD DE, 0x0000 + LD B, 8 + +.init_ram_dsk: + PUSH BC + CALL MON.ram_disk_write + POP BC + INC DE + DEC B + JP NZ, .init_ram_dsk + + ; Init user to 0 and drive to 0 + XOR A + LD (cur_user_drv), A + + ; init bios variables + CALL bios_init_ilv ; r8 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + JP bios_wboot + +;bios_wboot: +; LD SP, bios_stack + +; r8 >> +bios_init_ilv: + LD HL, bios_var0 + LD DE, bios_ini_vals + LD C, 13 + CALL mov_dlhe_c + LD A, (bios_var05) ; 0x50 + LD (bios_var2), A + RET +; << + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: ; r8 + + LD HL, (bios_var0) + LD DE, 0x5556 ; 0xaaaa + 0x5556 = 0x10000 + ADD HL, DE + LD A, H + OR L + CALL NZ, bios_init_ilv ; init if not initialized before + LD SP, bios_stack + + ; Move CPP from 0xC000 to 0xB200 + LD HL, CCP_DST_ADDR + LD DE, CCP_SRC_ADDR + LD BC, CCP_SIZE +.move_ccp: + LD A, (DE) + LD (HL), A + INC DE + INC HL + DEC BC + LD A, B + OR C + JP NZ, .move_ccp + + ; Init variables with zeroes + LD HL, CPM_VARS.cpm_vars_start + LD BC, CPM_VARS.ccp_vars_size ; 213 + +.clr_cpm_vars: + LD (HL), 0x0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .clr_cpm_vars + + LD A, FILE_DELETED + LD (CPM_VARS.bdos_efcb), A ; mark empty FCB + LD A, low dma_buffer ; 0x80 + LD (CPM_VARS.bdos_dmaad), A ; 0x0080 + + ; Move DPH + DPB to RAM + LD HL, CPM_VARS.DPH_RAM + LD DE, dph_disk_a + LD BC, DPB_END-dph_disk_a ; 0x39 -> 57d + +.move_dph: + LD A, (DE) + LD (HL), A + INC HL + INC DE + DEC BC + LD A, B + OR C + JP NZ, .move_dph + + LD BC, dma_buffer ; DMA default buffer addr + CALL setdma_f + + ; Setup JP to Warm boot after CPU reset + LD A, JP_OPCODE + LD (warm_boot), A + LD HL, wboot_f + LD (warm_boot_addr), HL + + ; Setup JP to BDOS entrance + LD (jp_bdos_enter), A + LD HL, CCP_RAM.bdos_enter_jump + LD (bdos_ent_addr), HL + + LD HL, CPM_VARS.DPB_A_RAM + LD C, 0xf + LD DE, dpb_ram + LD A, (disk_a_size+1) + OR A + JP Z, set_curr_dpb + LD DE, dpb_empty + +set_curr_dpb: + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_B_RAM + LD C, 0xf + LD DE, dpb_flop_360k + LD A, (disk_b_size+1) + CP 0x1 + JP Z, .l1 + LD DE, dpb_flop_720k +.l1 + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_C_RAM + LD C, 0xf + LD A, (disk_c_size+1) + CP 0x2 + JP Z, .l2 + LD DE, dpb_flop_360k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_720k + JP .l3 +.l2 + LD DE, dpb_flop_720k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_360k +.l3 + CALL mov_dlhe_c + XOR A + LD (CPM_VARS.slicer_has_data),A + LD (CPM_VARS.slicer_uninited_count),A + LD A,(cur_user_drv) + LD C,A + ; go to CCP + JP CCP_DST_ADDR + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +mov_dlhe_c: + LD A,(DE) ; [DE]->[HL] + LD (HL),A + INC HL + INC DE + DEC C + JP NZ, mov_dlhe_c + RET + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD HL, 0x00 + LD A, C + CP CTRL_C + RET NC + + LD (CPM_VARS.cur_disk), A + OR A + JP Z, .get_dph_addr ; skip next for disk 0 - RAM disk + LD A, E ; selected disk map + AND 0x1 ; bit 0 is set if disk already selected + JP NZ, .get_dph_addr + ; Reset disk + LD (CPM_VARS.slicer_has_data), A + LD (CPM_VARS.slicer_uninited_count), A + ; calc DPH address of new drive +.get_dph_addr: + LD L, C + LD H, 0x0 + ADD HL, HL ; *2 + ADD HL, HL ; *4 + ADD HL, HL ; *8 + ADD HL, HL ; *16 (size of DBH) + LD DE, CPM_VARS.DPH_RAM + ADD HL, DE + RET + +; ------------------------------------------------------- +; move to track 00 +; ------------------------------------------------------- +home: + LD A, (CPM_VARS.cur_disk) + OR A + JP Z, .is_default + LD A, (CPM_VARS.slicer_need_save) + OR A + JP NZ, .is_default + LD (CPM_VARS.slicer_has_data), A ; set to 0, no data + +.is_default: + LD C, 0 ; set track to 0 + +; ------------------------------------------------------- +; set track address (0..76) for subsequent read/write +; ------------------------------------------------------- +set_trk: + LD HL, CPM_VARS.curr_track + LD (HL), C + RET + +; ------------------------------------------------------- +; set sector address (1,..., 26) for subsequent read/write +; ------------------------------------------------------- +set_sec: + LD HL, CPM_VARS.curr_sec + LD (HL), C + RET + +; ------------------------------------------------------- +; set subsequent dma address (initially 80h) +; ------------------------------------------------------- +set_dma: + LD L, C + LD H, B + LD (CPM_VARS.dma_addr), HL + RET + +sec_tran: + LD L, C + LD H, B + RET + +; ------------------------------------------------------- +; read track/sector to preset dma address +; ------------------------------------------------------- +read: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ, read_phys ; for physical disk use special routine + CALL ram_disk_calc_addr + CALL MON.ram_disk_read + XOR A + RET + +; ------------------------------------------------------- +; write track/sector from preset dma address +; ------------------------------------------------------- +write: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ,write_phys + CALL ram_disk_calc_addr + CALL MON.ram_disk_write + XOR A + RET + +; ------------------------------------------------------- +; Calculate address for current sector and track +; ------------------------------------------------------- +ram_disk_calc_addr: + LD HL, CPM_VARS.curr_track + ; HL = cur_track * 16 + LD L, (HL) + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ; DE = HL + cur_sec + EX DE, HL + LD HL, CPM_VARS.curr_sec + LD L, (HL) + LD H, 0x0 + ADD HL, DE + EX DE, HL + ; store address + LD HL, (CPM_VARS.dma_addr) + RET + +read_phys: + CALL read_phys_op + RET + +write_phys: + CALL write_phys_op + RET + +read_phys_op: + XOR A + ; reset counter + LD (CPM_VARS.slicer_uninited_count), A + LD A, 0x1 + LD (CPM_VARS.tmp_slicer_operation), A ; 0 - write; 1 - read + LD (CPM_VARS.tmp_slicer_can_read), A ; enable read fron disk + LD A, 0x2 + LD (CPM_VARS.tmp_slicer_flush), A ; disable flush data to disk + JP base_read_write + +write_phys_op: + XOR A + LD (CPM_VARS.tmp_slicer_operation), A + LD A, C + LD (CPM_VARS.tmp_slicer_flush), A + CP 0x2 + JP NZ, .mode_ne_2 + LD A, 0x10 ; 2048/128 + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_uninited_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_uninited_track), A + LD A, (CPM_VARS.curr_sec) + LD (CPM_VARS.slicer_uninited_sector_128), A +.mode_ne_2: + LD A, (CPM_VARS.slicer_uninited_count) + OR A + JP Z, slicer_read_write + DEC A + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_uninited_disk + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_uninited_track + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_sec) + LD HL, CPM_VARS.slicer_uninited_sector_128 + CP (HL) + JP NZ, slicer_read_write + INC (HL) + LD A, (HL) + CP 36 ; Sectors per track + JP C, .no_inc_track + LD (HL), 0x0 + LD A, (CPM_VARS.slicer_uninited_track) + INC A + LD (CPM_VARS.slicer_uninited_track), A + +.no_inc_track: + XOR A + LD (CPM_VARS.tmp_slicer_can_read), A + JP base_read_write + +; -------------------------------------------------- +slicer_read_write: + XOR A + LD (CPM_VARS.slicer_uninited_count), A + INC A + LD (CPM_VARS.tmp_slicer_can_read), A + +; -------------------------------------------------- +base_read_write: + XOR A + LD (CPM_VARS.tmp_slicer_result), A + LD A, (CPM_VARS.curr_sec) + OR A + RRA + OR A + RRA + LD (CPM_VARS.tmp_slicer_real_sector), A + LD HL, CPM_VARS.slicer_has_data + LD A, (HL) + LD (HL), 0x1 + OR A + JP Z, .no_data + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_disk + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_track + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD HL, CPM_VARS.slicer_real_sector + CP (HL) + JP Z,calc_sec_addr_in_bfr +.pos_diff: + LD A, (CPM_VARS.slicer_need_save) + OR A + CALL NZ, slicer_save_buffer ; save buffer if needed +.no_data: + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_track), A + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD (CPM_VARS.slicer_real_sector), A + LD A, (CPM_VARS.tmp_slicer_can_read) + OR A + CALL NZ,slicer_read_buffer + XOR A + LD (CPM_VARS.slicer_need_save), A + +; -------------------------------------------------- +calc_sec_addr_in_bfr: + LD A, (CPM_VARS.curr_sec) + AND 0x3 + LD L, A + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL ; *128 + LD DE, CPM_VARS.slicer_buffer + ADD HL, DE + EX DE, HL + LD HL, (CPM_VARS.dma_addr) + LD C, 0x80 + LD A, (CPM_VARS.tmp_slicer_operation) + OR A + JP NZ, .no_save + LD A, 0x1 + LD (CPM_VARS.slicer_need_save), A + EX DE, HL +.no_save: + LD A, (DE) + INC DE + LD (HL), A + INC HL + DEC C + JP NZ, .no_save + LD A, (CPM_VARS.tmp_slicer_flush) + CP 0x1 + LD A, (CPM_VARS.tmp_slicer_result) + RET NZ + OR A + RET NZ + XOR A + LD (CPM_VARS.slicer_need_save), A + CALL slicer_save_buffer + LD A, (CPM_VARS.tmp_slicer_result) + RET + +; -------------------------------------------------- +slicer_save_buffer: + CALL slicer_get_floppy_args + LD C, 0xA6 ; VG93 CMD + CALL MON.write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x86 ; VG93 CMD + CALL MON.read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_get_floppy_args: + LD HL, CPM_VARS.tmp_buff9 + LD A, (CPM_VARS.slicer_disk) + DEC A + JP Z, .non_interleave + LD HL, interleave_0 +.non_interleave + LD A, (CPM_VARS.slicer_real_sector) + ADD A, L + LD L, A + LD E, (HL) + LD A, (CPM_VARS.slicer_track) + LD D, A + LD HL, CPM_VARS.slicer_buffer + LD A, (CPM_VARS.slicer_disk) + RET + + +; ------------------------------------------------------- +; Print zerro ended string +; Inp: HL -> string +; ------------------------------------------------------- +print_strz: + LD A, (HL) + OR A + RET Z + LD C, A + PUSH HL + CALL conout_f + POP HL + INC HL + JP print_strz + +msg_hello: + DB ASCII_ESC, "60" ; Режим 32x18 60 + DB ASCII_ESC, "83" + DB ASCII_ESC, "5!%" + DB ASCII_ESC, "41" + DB ASCII_ESC, "1", 0x16, 0xe2, 0xe2, 0xfc, 0x01 + DB ASCII_ESC, "40" + DB ASCII_ESC, "1", 0x1e, 0xe6, 0xdb, 0xf8, 0x01 + DB ASCII_ESC, "43" + DB "OKEAH-240 CP/M (V2.2) REL.8 \r\n\n" + DB ASCII_ESC, "42", 0 + + +; -------------------------------------------------- +; Disk parameters blocks in ROM (DPBs) +; Tables of memory that describe the characteristics +; of discs on our system. There is one DPB for each +; disc type + +; ---------------------------------------- +; Block size | No of sectors | BSH | BLM | +; ---------------------------------------- +; 1K | 8 | 3 | 7 | +; 2K | 16 | 4 | 15 | +; 4K | 32 | 5 | 31 | +; 8K | 64 | 6 | 63 | +; 16K | 128 | 7 | 127 | +; ---------------------------------------- + +; ------------------------------------- +; Block size| Extent mask (EXM) | +; | Small disk | Large disk | +; ------------------------------------- +; 2K | 1 | 0 | +; 4K | 3 | 1 | +; 8K | 7 | 3 | +; 16K | 15 | 7 | +; ------------------------------------- +; CKS - number of dir sectors to check before write, 0 for HDD + +; For RAM-Disk 64k +dpb_ram: + DW 0010h ; SPT Sector (128b) per track (16d) + DB 03h ; BSH 1k + DB 07h ; BLM 1k; Allocation block size = (BLM + 1) * 128 = 1k + DB 00h ; EXM extent mask + DW 003Fh ; DSM Disk size blocks - 1 (63d) + DW 001Fh ; DRM Dir elements - 1 (31d) + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0008h ; CKS checksum vector size (8 sectors=1k) + DW 0000h ; OFF (tracks reserved for system) + +dpb_empty: + DS 15, 0xff + +; For FLOPPY 720k +dpb_flop_720k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 00h ; EXM extent mask + DW 359 ; DSM Disk size blocks - 1 (359d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +; For FLOPPY 360k +dpb_flop_360k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 01h ; EXM extent mask + DW 179 ; DSM Disk size blocks - 1 (179d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +bios_ini_vals: + DB 0xaa, 0xaa, 0, 0xff, 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +dph_disk_a: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + ; 0xda00 + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_A_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_A ; Check Vector pointer + DW CPM_VARS.AL_MAP_A ; Allocation map pointer + +; Disk B Floppy +dph_disk_b: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_B_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_B ; Check Vector pointer + DW CPM_VARS.AL_MAP_B ; Allocation map pointer + +; Disk C Floppy +dph_disk_c: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_C_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + + + +res_data: ; 0xda28 + DB 1,8,6,4,2,9,7,5,3 +DPB_END EQU $ + DB 0x0e, 3 + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT bios_wboot = 0xd6a8 + ASSERT set_curr_dpb = 0xd722 + ASSERT sel_disk = 0xd781 + ASSERT home = 0xd7a7 + ASSERT ram_disk_calc_addr = 0xd7eb + ASSERT write_phys_op = 0xd81e + ASSERT base_read_write = 0xd88a + ASSERT calc_sec_addr_in_bfr = 0xd8e4 + ASSERT slicer_save_buffer = 0xd928 + ASSERT print_strz = 0xd95e + ASSERT msg_hello = 0xd96b + ASSERT dpb_ram = 0xd9af + ASSERT dph_disk_a = 0xd9f8 + ASSERT res_data = 0xda28 + +FILLER: + DS FILL_SIZE, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_099942fc/ccp_ram.asm b/CPM_v2.2_r8_099942fc/ccp_ram.asm new file mode 100644 index 0000000..a2ecada --- /dev/null +++ b/CPM_v2.2_r8_099942fc/ccp_ram.asm @@ -0,0 +1,1525 @@ +; ====================================================== +; Ocean-240.2 +; CP/M V2.2 R8. +; 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 + +ccp_chg_drive: ; change drive flag, 0 - no change + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + + +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 |" + + ; Check integrity + ASSERT ccp_cmdline_back = 0xb986 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/ccp_rom.asm b/CPM_v2.2_r8_099942fc/ccp_rom.asm new file mode 100644 index 0000000..b859801 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/ccp_rom.asm @@ -0,0 +1,901 @@ +; ======================================================= +; 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_cold_start + +; -------------------------------------------------- +; 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, (bios_var3) + 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, 0x4 + LD B, 0x0 + +.next_d: + LD E, 0x4 + +.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, 35 + JP jp_bdos_enter + +; ------------------------------------------------------- +; Read data from Tape +; ------------------------------------------------------- +ccp_read: + CALL BIOS.const_f + OR A + JP Z, .no_key ; chk key pressed + CALL BIOS.conin_f + JP ccp_read +.no_key: + CALL CCP_RAM.ccp_bdos_drv_allreset + CALL CCP_RAM.ccp_out_crlf + + ; wait tape block header + LD A, 100 + CALL BIOS.tape_wait_f + OR A + JP NZ, .key_pressed1 + + ; try to load header 8 times + LD E, 8 +.load8_cont: + LD HL, dma_buffer + CALL BIOS.tape_read_f + CP 0x4 + JP Z, .key_pressed1 + OR A + JP Z, .hdr_id_chk + DEC E + JP NZ, .load8_cont + JP .cp_load_err_what + + ; Check blk ID, will be 0x8000 +.hdr_id_chk: + LD A, B + CP 0x80 + JP NZ, .load8_cont + LD A, C + OR A + JP NZ, .load8_cont + ; set end of file name strZ + XOR A + LD (dma_buffer+12), A + LD DE, dma_buffer + LD HL, CCP_RAM.ccp_inp_line ;= 2020h + LD (CCP_RAM.ccp_inp_line_addr), HL ;= B208h + +.cp_name_char: + LD A, (DE) + OR A + JP Z, .name_copied + LD (HL), A ;= 2020h + INC HL + INC DE + JP .cp_name_char +.name_copied: + LD (HL), 0x0 ; mark end of strZ + CALL CCP_RAM.ccp_cv_first_to_fcb + JP NZ, CCP_RAM.print_syn_err + + ; Output file name to screen + LD HL, CCP_RAM.ccp_current_fcb_fn + LD B, 11 + +.name_output: + LD A, (HL) + AND 01111111b ; 7bit ASCII + LD C, A + CALL BIOS.conout_f + INC HL + DEC B + JP NZ, .name_output + ; Out ' ' + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL CCP_RAM.ccp_open_cur_fcb + JP Z, .recreate_f + LD HL,msg_ex ;= "*EX* " + CALL ccp_out_str_z + LD HL, CCP_RAM.ccp_current_fcb_fn+8 + ; '$$$' at the end of buffer + LD A, '$' + LD (HL), A + INC HL + LD (HL), A + INC HL + LD (HL), A + + ; delete file if exists and create new +.recreate_f: + LD DE, CCP_RAM.ccp_current_fcb + PUSH DE + CALL CCP_RAM.ccp_bdos_era_file + POP DE + CALL CCP_RAM.ccp_bdos_make_f + JP Z, CCP_RAM.ccp_no_space ; out NO SPACE if err + XOR A + LD (CCP_RAM.ccp_current_fcb_fn+31), A + + ; Load block with ID=1 + LD DE, 1 +.fin_load: + LD HL, dma_buffer + PUSH DE + +.blk_ld_rep: + CALL BIOS.tape_read_f + CP 4 + JP Z, .key_pressed2 + OR A + JP Z, .blk_id_chk + LD A, (CCP_RAM.ccp_current_fcb_fn+31) + AND 0x7f + JP NZ, .cp_read_cs + +.blk_id_chk: + LD A, C + OR B + JP Z, .blk_ld_rep + + ; Check id for 0xFFFF - last blk + LD A, C + AND B + CP 0xff + JP Z, .blk_id_last + ; Check ID for DE + LD A, C + CP E + JP NZ, .blk_ne_id + LD A, B + CP D + JP NZ, .blk_ne_id + ; Ok, write blk to disk + LD DE, dma_buffer + CALL CCP_RAM.ccp_bdos_dma_set + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_f_write + POP DE + JP NZ,ccp_del_f_no_space + INC DE + JP .fin_load + +.cp_load_err_what: + LD HL,msg_what ;= "WHAT?\a" + CALL ccp_out_str_z + JP .load_restart + +.cp_read_cs: + POP BC + LD HL, msg_cs ;= "\a", 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 diff --git a/CPM_v2.2_r8_099942fc/cpm.asm b/CPM_v2.2_r8_099942fc/cpm.asm new file mode 100644 index 0000000..c2ea78d --- /dev/null +++ b/CPM_v2.2_r8_099942fc/cpm.asm @@ -0,0 +1,44 @@ +; ====================================================== +; Ocean-240.2 +; CP/M Combine file. Includes all sources to build +; ROM 0xC000 +; +; Disassembled by Romych 2025-09-09 +; ====================================================== + + DEFINE BUILD_ROM + + DEVICE NOSLOT64K + +; +; |-----------|---------------|-----------|--------------------------------------| +; | OFFSET | SIZE | Module | Memory Address | +; |-----------|---------------|-----------|--------------------------------------| +; | 0x0000 | 2048 (0x800) | CCP_RAM | 0xC000..0xC7FF -> RAM 0xB200..0xB5FF | +; | 0x0800 | 3584 (0xE00) | BDOS | 0xC800.. | +; | 0x1600 | 1024 (0x400) | BIOS | 0xD600..D9FF | +; | 0x1B00 | 1280 (0x500) | CCP_ROM | 0xDB00..DFFF | +; |-----------|---------------|-----------|--------------------------------------| +; + + DISPLAY "| Module | Offset | Size | Free |" + DISPLAY "|-------------|---------|--------|--------|" + + + OUTPUT cpm-C000.bin + ;LABELSLIST "cpm.labels" + ;CSPECTMAP "cpm.map" + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + OUTPUT variables.bin + INCLUDE "cpm_vars.inc" + OUTEND + + +END \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/cpm_vars.inc b/CPM_v2.2_r8_099942fc/cpm_vars.inc new file mode 100644 index 0000000..ea33312 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/cpm_vars.inc @@ -0,0 +1,219 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM_VARS - BIOS + BDOS + CPP variables +; RAM Range: 0xBA09-0xBF00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _CPM_VARS + DEFINE _CPM_VARS + + MODULE CPM_VARS + ORG 0xBA09 + +cpm_vars_start EQU $ +; Filled by zeroes by BIOS at cold boot +; until ccp_vars_end + +; Output disabel if non zero +bdos_no_outflag DB 0 +bdos_strtcol DB 0 +bdos_column DB 0 + +; Copy output to printer in non zero +bdos_prnflag DB 0 +bdos_kbchar DB 0 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DW 0 ; 0xba0e + +; BDOS Stack + DS 48, 0 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DB 0x00 ; 0xba40 + +; current disk number +bdos_curdsk DB 0x00 + +; reg DE (pointer) value to call BDOS fn +bdos_info DW 0x0000 ; 0xba42 + +; Address value to return +bdos_aret DW 0x0000 ; 0xba44 +; Empty FCB +bdos_efcb DB 0x00 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DW 0x0000 ; 0xba47 +; Drive login vector +bdos_dlog DW 0x0000 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DW 0x0000 ; 0xba4b = 0x80 at init +bdos_cdrmax DW 0x0000 ; 0xba4d +; Current track +bdos_curtrk DW 0x0000 ; 0xba4f +; Current record (sector) +bdos_currec DW 0x0000 ; 0xba51 +; Address of user buffer +bdos_buffa DW 0x0000 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DW 0x0000 ; 0xba55 +; Calculated checksum +bdos_cksum DW 0x0000 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DW 0x0000 ; 0xba59 +; Sectors per track +dbos_sectpt DW 0x0000 ; 0xba5b +bdos_blkshf DB 0x00 ; 0xba5d +bdos_blmsk DB 0x00 ; 0xba5e +bdos_extmsk DB 0x00 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DW 0x0000 ; 0xba60 +bdos_dirmax DW 0x0000 ; 0xba62 +; Size of directory +bdos_dirblk DW 0x0000 ; 0xba64 +bdos_alloc1 DW 0x0000 ; 0xba66 +; First track offset +bdos_offset DW 0x0000 ; 0xba68 +; Disk traslation vector address +bdos_tranv DW 0x0000 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DB 0x00 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DB 0x00 ; 0xba6d +bdos_dirloc DB 0x00 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DB 0x00 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DB 0x00 ; 0xba70 +bdos_dminx DB 0x00 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DB 0x00 ; 0xba72 +; Address of buffer where to search +bdos_searcha DW 0x0000 ; 0xba73 + DW 0x0000 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DB 0x00 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DB 0x00 ; 0xba78 +bdos_olddsk DB 0x00 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DB 0x00 ; 0xba7a +; Number of records in extent +bdos_rcount DB 0x00 ; 0xba7b +bdos_extval DB 0x00 ; 0xba7c +; Record number to read +bdos_vrecord DW 0x0000 ; 0xba7d +; Record to access +bdos_arecord DW 0x0000 ; 0xba7f +bdos_arecord1 DW 0x0000 ; 0xba81 +bdos_dptr DB 0x00 ; 0xba83 +bdos_dcnt DW 0x00 ; 0xba84 +bdos_drec DS 20, 0x00 ; 0xba86 +tmp_dir_total DW 0x0000 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DW 0x0000 ; 0xba9c + DS 22, 0 +ccp_safe_stack + DW 0x0000 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_fcb +ccp_fcb_dr DB 0 ; 0xbab6 drive letter to write file +ccp_fcb_fn DS 8,0 ; 0xbab7 file name +ccp_fcb_ft DS 3,0 ; file type +ccp_fcb_ext DB 0 ; extent +ccp_fcb_s1 DB 0 +ccp_fcb_s2 DB 0 ; extent hi bits +ccp_fcb_rc DB 0 +ccp_fcb_al DS 16, 0 ; Second half of FCB +ccp_fcb_cr DB 0x00 ; Current record +ccp_fcb_rn DB 0x00 ; Random access record number + DW 0x00 + DW 0x00 + DW 0x00 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 ; 0xbaee +DPH2_RAM DS 16, 0 ; 0xbafe +tmp_buff9 DS 9, 0 +DPB_A_RAM DS 15, 0 ; 0xbb17 +DPB_B_RAM DS 15, 0 ; 0xbb26 +DPB_C_RAM DS 15, 0 ; 0xbb35 + +; -------------------------------------------------- +cur_disk DB 0x00 ; 0xbb44 +dma_addr DW 0x0000 ; 0xbb46 +curr_track DB 0x00 ; 0xbb47 +curr_sec DB 0x00 ; 0xbb48 +slicer_disk DB 0x00 ; 0xbb49 +slicer_track DB 0x00 ; 0xbb4a +slicer_real_sector DB 0x00 ; 0xbb4b +tmp_slicer_real_sector DB 0x00 ; 0xbb4c +slicer_has_data DB 0x00 ; 0xbb4d +slicer_need_save DB 0x00 ; 0xbb4e +slicer_uninited_count DB 0x00 ; 0xbb4f +slicer_uninited_disk DB 0x00 ; 0xbb42 +slicer_uninited_track DB 0x00 ; 0xbb43 +slicer_uninited_sector_128 DB 0x00 ; 0xbb44 +tmp_slicer_result DB 0x00 ; 0xbb45 +tmp_slicer_can_read DB 0x00 ; 0xbb46 +tmp_slicer_operation DB 0x00 ; 0xbb47 +tmp_slicer_flush DB 0x00 ; 0xbb48 + +; Directory buffer used by BDOS for directory operations +dir_buffer DS DIR_BUFF_SIZE, 0 ; 0xbb49 + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31, 0 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16, 0 ; 0xbbf6 + +; Allocation map for drive B +AL_MAP_B DS 45, 0 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 32, 0 ; 0xbc33 + +; Allocation map for drive C +AL_MAP_C DS 45, 0 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 32, 0 ; 0xbc80 + DS 96, 0 + +slicer_buffer DS 512, 0 ; 0xbd00 + +cpm_vars_end EQU $ + +ccp_vars_size EQU ccp_vars_end-cpm_vars_start +cpm_vars_size EQU cpm_vars_end-cpm_vars_start + + ; check integrity of original ROM + ASSERT tmp_dir_total = 0xba9a + ASSERT saved_stack_ptr = 0xba9c + ASSERT ccp_fcb_fn = 0xbab7 + ASSERT DPH_RAM = 0xbade + ASSERT cur_disk = 0xbb44 + ASSERT slicer_uninited_count = 0xbb4f + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + DISPLAY "cur_disk:",/H,cur_disk + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r8_099942fc/equates.inc b/CPM_v2.2_r8_099942fc/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/io.inc b/CPM_v2.2_r8_099942fc/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/CPM_v2.2_r8_099942fc/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/mon_entries.inc b/CPM_v2.2_r8_099942fc/mon_entries.inc new file mode 100644 index 0000000..b607096 --- /dev/null +++ b/CPM_v2.2_r8_099942fc/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON + DEFINE _MON + + MODULE MON + +mon_init EQU 0xe000 +mon_cold_start EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mon_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_099942fc/ram.inc b/CPM_v2.2_r8_099942fc/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/CPM_v2.2_r8_099942fc/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/.gitignore b/CPM_v2.2_r8_2e4a7b71/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/CPM_v2.2_r8_2e4a7b71/.vscode/extensions.json b/CPM_v2.2_r8_2e4a7b71/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/CPM_v2.2_r8_2e4a7b71/.vscode/tasks.json b/CPM_v2.2_r8_2e4a7b71/.vscode/tasks.json new file mode 100644 index 0000000..f71ba83 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make CPM (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=cpm.sld", + "--sym=cpm.labels", + "--lstlab=sort", + "--lst=cpm.lst", + "--raw=cpm.obj", + "--fullpath", + "cpm.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/BIN/CPM_REL_8_2e4a7b71.BIN b/CPM_v2.2_r8_2e4a7b71/BIN/CPM_REL_8_2e4a7b71.BIN new file mode 100644 index 0000000..02fad76 Binary files /dev/null and b/CPM_v2.2_r8_2e4a7b71/BIN/CPM_REL_8_2e4a7b71.BIN differ diff --git a/CPM_v2.2_r8_2e4a7b71/README.md b/CPM_v2.2_r8_2e4a7b71/README.md new file mode 100644 index 0000000..609e076 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/README.md @@ -0,0 +1,11 @@ +# Ocean-240.2 ROM Sources CP/M (V2.2) REL.8' checksum 2e4a7b71 + +Source codes of CP/M (V2.2) REL.8' version for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +* 192k RAM drive + +## Compile: + sjasmplus --sld=cpm.sld --sym=cpm.labels --raw=cpm.obj --fullpath cpm.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/CPM_v2.2_r8_2e4a7b71/bdos.asm b/CPM_v2.2_r8_2e4a7b71/bdos.asm new file mode 100644 index 0000000..3d21d38 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/bdos.asm @@ -0,0 +1,3041 @@ +; ======================================================= +; 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 + DW bdos_get_con_st ; fn11 + DW bdos_get_version ; fn12 + DW bdos_reset_disks ; fn13 + DW bdos_select_disk ; fn14 + DW bdos_open_file ; fn15 + DW bdos_close_file ; fn16 + DW bdos_search_first ; fn17 + DW bdos_search_next ; fn18 + DW bdos_rm_file ; fn19 + DW bdos_read_file ; fn20 + DW bdos_write_file ; fn21 + DW bdos_make_file ; fn22 + DW bdos_ren_file ; fn23 + DW bdos_get_login_vec ; fn24 + DW bdos_get_cur_drive ; fn25 + DW bdos_set_dma_addr ; fn26 + DW bdos_get_alloc_addr ; fn27 + DW bdos_set_ro ; fn28 + DW bdos_get_wr_protect ; fn29 + DW bdos_set_attr ; fn30 + DW bdos_get_dpb ; fn31 + DW bdos_set_user ; fn32 + DW bdos_rand_read ; fn33 + DW bdos_rand_write ; fn34 + DW bdos_compute_fs ; fn35 + DW bdos_set_random ; fn36 + DW bdos_reset_drives ; fn37 + DW bdos_not_impl ; fn38 (Access Drive) + DW bdos_not_impl ; fn39 (Free Drive) + DW bdos_rand_write_z ; fn40 + +; ------------------------------------------------------- +; 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.dbos_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.dbos_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.dbos_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 + +; ------------------------------------------------------- +; 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 diff --git a/CPM_v2.2_r8_2e4a7b71/bios.asm b/CPM_v2.2_r8_2e4a7b71/bios.asm new file mode 100644 index 0000000..dedb8dc --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/bios.asm @@ -0,0 +1,823 @@ +; ======================================================= +; Ocean-240.2 +; +; CP/M BIOS +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "mon_entries.inc" + + IFNDEF BUILD_ROM + OUTPUT bios.bin + ENDIF + + MODULE BIOS + + ORG 0xD600 + +; ------------------------------------------------------- +; BIOS JUMP TABLE +; ------------------------------------------------------- +boot_f: + JP bios_boot +wboot_f: + JP bios_wboot + +; ------------------------------------------------------- +; console status to reg-a +; ------------------------------------------------------- +const_f: + JP MON.non_con_status + +; ------------------------------------------------------- +; console character to reg-a +; ------------------------------------------------------- +conin_f: + JP MON.mon_con_in + +; ------------------------------------------------------- +; console character from c to console out +; ------------------------------------------------------- +conout_f: + JP MON.mon_con_out + +; ------------------------------------------------------- +; list device out +; ------------------------------------------------------- +list_f: + JP MON.mon_char_print + +; ------------------------------------------------------- +; punch device out +; ------------------------------------------------------- +punch_f: + JP MON.mon_serial_out + +; ------------------------------------------------------- +; reader character in to reg-a +; ------------------------------------------------------- +reader_f: + JP MON.mon_serial_in + +; ------------------------------------------------------- +; move to home position, treat as track 00 seek +; ------------------------------------------------------- +home_f: + JP home + +; ------------------------------------------------------- +; select disk given by register c +; ------------------------------------------------------- +seldsk_f: + JP sel_disk +settrk_f: + JP set_trk +setsec_f: + JP set_sec + +; ------------------------------------------------------- +; Set DMA address from BC +; ------------------------------------------------------- +setdma_f: + JP set_dma +read_f: + JP read +write_f: + JP write +listst_f: + JP list_st +sectran_f: + JP sec_tran + +; ------------------------------------------------------- +; Reserved +; ------------------------------------------------------- + JP warm_boot + JP warm_boot + +; ------------------------------------------------------- +; Tape read +; ------------------------------------------------------- +tape_read_f: + JP MON.mon_tape_read + +; ------------------------------------------------------- +; Tape write +; ------------------------------------------------------- +tape_write_f: + JP MON.mon_tape_write + +; ------------------------------------------------------- +; Tape wait block +; ------------------------------------------------------- +tape_wait_f: + JP MON.mon_tape_wait + + JP warm_boot ; r8 + +disk_a_size DW 0x00C0 ; 192k disk A size +disk_b_size DW 0x02d0 ; 720 disk B size +disk_c_size DW 0x02d0 ; 720 disk C size +bios_var04 DB 0x50 +bios_var05 DB 0x50 + +; ------------------------------------------------------- +; cold start +; ------------------------------------------------------- +bios_boot: + LD HL, (bdos_ent_addr) ; 0xba06 + LD DE, 0x10000-CCP_RAM.bdos_enter_jump ; 0x45fa + ADD HL, DE ; 0xba06+0x45fa=10000 + LD A, H + OR L + JP Z, bios_signon + + ; >> r8 + LD HL, (bios_var0) ; if bios_var0 = 0xaaaa (initialized) + LD DE, 0x5556 + ADD HL, DE ; 0xaaaa+0x5556=0x10000 if initialized + LD A, H + OR L + JP Z, bios_signon ; if initialized, go to logon, skip init + ; << + + ; Init DMA buffer + LD HL, dma_buffer ; 0x8000 + LD B, DMA_BUFF_SIZE ; 0x80 +.init_dma_buff: + LD (HL), FILE_DELETED ; 0xE5 + INC HL + DEC B + JP NZ, .init_dma_buff + + ; Init RAM disk + LD HL, dma_buffer + LD DE, 0x0000 + LD B, 8 + +.init_ram_dsk: + PUSH BC + CALL MON.ram_disk_write + POP BC + INC DE + DEC B + JP NZ, .init_ram_dsk + + ; Init user to 0 and drive to 0 + XOR A + LD (cur_user_drv), A + + ; init bios variables + CALL bios_init_ilv ; r8 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + JP bios_wboot + +;bios_wboot: +; LD SP, bios_stack + +; r8 >> +bios_init_ilv: + LD HL, bios_var0 + LD DE, bios_ini_vals + LD C, 13 + CALL mov_dlhe_c + LD A, (bios_var05) ; 0x50 + LD (bios_var2), A + RET +; << + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: ; r8 + + LD HL, (bios_var0) + LD DE, 0x5556 ; 0xaaaa + 0x5556 = 0x10000 + ADD HL, DE + LD A, H + OR L + CALL NZ, bios_init_ilv ; init if not initialized before + LD SP, bios_stack + + ; Move CPP from 0xC000 to 0xB200 + LD HL, CCP_DST_ADDR + LD DE, CCP_SRC_ADDR + LD BC, CCP_SIZE +.move_ccp: + LD A, (DE) + LD (HL), A + INC DE + INC HL + DEC BC + LD A, B + OR C + JP NZ, .move_ccp + + ; Init variables with zeroes + LD HL, CPM_VARS.cpm_vars_start + LD BC, CPM_VARS.ccp_vars_size ; 213 + +.clr_cpm_vars: + LD (HL), 0x0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .clr_cpm_vars + + LD A, FILE_DELETED + LD (CPM_VARS.bdos_efcb), A ; mark empty FCB + LD A, low dma_buffer ; 0x80 + LD (CPM_VARS.bdos_dmaad), A ; 0x0080 + + ; Move DPH + DPB to RAM + LD HL, CPM_VARS.DPH_RAM + LD DE, dph_disk_a + LD BC, DPB_END-dph_disk_a ; 0x39 -> 57d + +.move_dph: + LD A, (DE) + LD (HL), A + INC HL + INC DE + DEC BC + LD A, B + OR C + JP NZ, .move_dph + + LD BC, dma_buffer ; DMA default buffer addr + CALL setdma_f + + ; Setup JP to Warm boot after CPU reset + LD A, JP_OPCODE + LD (warm_boot), A + LD HL, wboot_f + LD (warm_boot_addr), HL + + ; Setup JP to BDOS entrance + LD (jp_bdos_enter), A + LD HL, CCP_RAM.bdos_enter_jump + LD (bdos_ent_addr), HL + + LD HL, CPM_VARS.DPB_A_RAM + LD C, 0xf + LD DE, dpb_ram + LD A, (disk_a_size+1) + OR A + JP Z, set_curr_dpb + LD DE, dpb_empty + +set_curr_dpb: + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_B_RAM + LD C, 0xf + LD DE, dpb_flop_360k + LD A, (disk_b_size+1) + CP 0x1 + JP Z, .l1 + LD DE, dpb_flop_720k +.l1 + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_C_RAM + LD C, 0xf + LD A, (disk_c_size+1) + CP 0x2 + JP Z, .l2 + LD DE, dpb_flop_360k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_720k + JP .l3 +.l2 + LD DE, dpb_flop_720k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_360k +.l3 + CALL mov_dlhe_c + XOR A + LD (CPM_VARS.slicer_has_data),A + LD (CPM_VARS.slicer_uninited_count),A + LD A,(cur_user_drv) + LD C,A + ; go to CCP + JP CCP_DST_ADDR + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +mov_dlhe_c: + LD A,(DE) ; [DE]->[HL] + LD (HL),A + INC HL + INC DE + DEC C + JP NZ, mov_dlhe_c + RET + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD HL, 0x00 + LD A, C + CP CTRL_C + RET NC + + LD (CPM_VARS.cur_disk), A + OR A + JP Z, .get_dph_addr ; skip next for disk 0 - RAM disk + LD A, E ; selected disk map + AND 0x1 ; bit 0 is set if disk already selected + JP NZ, .get_dph_addr + ; Reset disk + LD (CPM_VARS.slicer_has_data), A + LD (CPM_VARS.slicer_uninited_count), A + ; calc DPH address of new drive +.get_dph_addr: + LD L, C + LD H, 0x0 + ADD HL, HL ; *2 + ADD HL, HL ; *4 + ADD HL, HL ; *8 + ADD HL, HL ; *16 (size of DBH) + LD DE, CPM_VARS.DPH_RAM + ADD HL, DE + RET + +; ------------------------------------------------------- +; move to track 00 +; ------------------------------------------------------- +home: + LD A, (CPM_VARS.cur_disk) + OR A + JP Z, .is_default + LD A, (CPM_VARS.slicer_need_save) + OR A + JP NZ, .is_default + LD (CPM_VARS.slicer_has_data), A ; set to 0, no data + +.is_default: + LD C, 0 ; set track to 0 + +; ------------------------------------------------------- +; set track address (0..76) for subsequent read/write +; ------------------------------------------------------- +set_trk: + LD HL, CPM_VARS.curr_track + LD (HL), C + RET + +; ------------------------------------------------------- +; set sector address (1,..., 26) for subsequent read/write +; ------------------------------------------------------- +set_sec: + LD HL, CPM_VARS.curr_sec + LD (HL), C + RET + +; ------------------------------------------------------- +; set subsequent dma address (initially 80h) +; ------------------------------------------------------- +set_dma: + LD L, C + LD H, B + LD (CPM_VARS.dma_addr), HL + RET + +sec_tran: + LD L, C + LD H, B + RET + +; ------------------------------------------------------- +; read track/sector to preset dma address +; ------------------------------------------------------- +read: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ, read_phys ; for physical disk use special routine + CALL ram_disk_calc_addr + CALL MON.ram_disk_read + XOR A + RET + +; ------------------------------------------------------- +; write track/sector from preset dma address +; ------------------------------------------------------- +write: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ,write_phys + CALL ram_disk_calc_addr + CALL MON.ram_disk_write + XOR A + RET + +; ------------------------------------------------------- +; Calculate address for current sector and track +; ------------------------------------------------------- +ram_disk_calc_addr: + LD HL, CPM_VARS.curr_track + ; HL = cur_track * 16 + LD L, (HL) + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ; DE = HL + cur_sec + EX DE, HL + LD HL, CPM_VARS.curr_sec + LD L, (HL) + LD H, 0x0 + ADD HL, DE + EX DE, HL + ; store address + LD HL, (CPM_VARS.dma_addr) + RET + +read_phys: + CALL read_phys_op + RET + +write_phys: + CALL write_phys_op + RET + +read_phys_op: + XOR A + ; reset counter + LD (CPM_VARS.slicer_uninited_count), A + LD A, 0x1 + LD (CPM_VARS.tmp_slicer_operation), A ; 0 - write; 1 - read + LD (CPM_VARS.tmp_slicer_can_read), A ; enable read fron disk + LD A, 0x2 + LD (CPM_VARS.tmp_slicer_flush), A ; disable flush data to disk + JP base_read_write + +write_phys_op: + XOR A + LD (CPM_VARS.tmp_slicer_operation), A + LD A, C + LD (CPM_VARS.tmp_slicer_flush), A + CP 0x2 + JP NZ, .mode_ne_2 + LD A, 0x10 ; 2048/128 + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_uninited_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_uninited_track), A + LD A, (CPM_VARS.curr_sec) + LD (CPM_VARS.slicer_uninited_sector_128), A +.mode_ne_2: + LD A, (CPM_VARS.slicer_uninited_count) + OR A + JP Z, slicer_read_write + DEC A + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_uninited_disk + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_uninited_track + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_sec) + LD HL, CPM_VARS.slicer_uninited_sector_128 + CP (HL) + JP NZ, slicer_read_write + INC (HL) + LD A, (HL) + CP 36 ; Sectors per track + JP C, .no_inc_track + LD (HL), 0x0 + LD A, (CPM_VARS.slicer_uninited_track) + INC A + LD (CPM_VARS.slicer_uninited_track), A + +.no_inc_track: + XOR A + LD (CPM_VARS.tmp_slicer_can_read), A + JP base_read_write + +; -------------------------------------------------- +slicer_read_write: + XOR A + LD (CPM_VARS.slicer_uninited_count), A + INC A + LD (CPM_VARS.tmp_slicer_can_read), A + +; -------------------------------------------------- +base_read_write: + XOR A + LD (CPM_VARS.tmp_slicer_result), A + LD A, (CPM_VARS.curr_sec) + OR A + RRA + OR A + RRA + LD (CPM_VARS.tmp_slicer_real_sector), A + LD HL, CPM_VARS.slicer_has_data + LD A, (HL) + LD (HL), 0x1 + OR A + JP Z, .no_data + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_disk + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_track + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD HL, CPM_VARS.slicer_real_sector + CP (HL) + JP Z,calc_sec_addr_in_bfr +.pos_diff: + LD A, (CPM_VARS.slicer_need_save) + OR A + CALL NZ, slicer_save_buffer ; save buffer if needed +.no_data: + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_track), A + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD (CPM_VARS.slicer_real_sector), A + LD A, (CPM_VARS.tmp_slicer_can_read) + OR A + CALL NZ,slicer_read_buffer + XOR A + LD (CPM_VARS.slicer_need_save), A + +; -------------------------------------------------- +calc_sec_addr_in_bfr: + LD A, (CPM_VARS.curr_sec) + AND 0x3 + LD L, A + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL ; *128 + LD DE, CPM_VARS.slicer_buffer + ADD HL, DE + EX DE, HL + LD HL, (CPM_VARS.dma_addr) + LD C, 0x80 + LD A, (CPM_VARS.tmp_slicer_operation) + OR A + JP NZ, .no_save + LD A, 0x1 + LD (CPM_VARS.slicer_need_save), A + EX DE, HL +.no_save: + LD A, (DE) + INC DE + LD (HL), A + INC HL + DEC C + JP NZ, .no_save + LD A, (CPM_VARS.tmp_slicer_flush) + CP 0x1 + LD A, (CPM_VARS.tmp_slicer_result) + RET NZ + OR A + RET NZ + XOR A + LD (CPM_VARS.slicer_need_save), A + CALL slicer_save_buffer + LD A, (CPM_VARS.tmp_slicer_result) + RET + +; -------------------------------------------------- +slicer_save_buffer: + CALL slicer_get_floppy_args + LD C, 0xA6 ; VG93 CMD + CALL MON.write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x86 ; VG93 CMD + CALL MON.read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_get_floppy_args: + LD HL, CPM_VARS.tmp_buff9 + LD A, (CPM_VARS.slicer_disk) + DEC A + JP Z, .non_interleave + LD HL, interleave_0 +.non_interleave + LD A, (CPM_VARS.slicer_real_sector) + ADD A, L + LD L, A + LD E, (HL) + LD A, (CPM_VARS.slicer_track) + LD D, A + LD HL, CPM_VARS.slicer_buffer + LD A, (CPM_VARS.slicer_disk) + RET + + +; ------------------------------------------------------- +; Print zerro ended string +; Inp: HL -> string +; ------------------------------------------------------- +print_strz: + LD A, (HL) + OR A + RET Z + LD C, A + PUSH HL + CALL conout_f + POP HL + INC HL + JP print_strz + +msg_hello: + DB ASCII_ESC, "60" ; Режим 32x18 60 + DB ASCII_ESC, "83" + DB ASCII_ESC, "5!%" + DB ASCII_ESC, "41" + DB ASCII_ESC, "1", 0x16, 0xe2, 0xe2, 0xfc, 0x01 + DB ASCII_ESC, "40" + DB ASCII_ESC, "1", 0x1e, 0xe6, 0xdb, 0xf8, 0x01 + DB ASCII_ESC, "43" + DB "OKEAH-240 CP/M (V2.2) REL.8'\r\n\n" + DB ASCII_ESC, "42", 0 + + +; -------------------------------------------------- +; Disk parameters blocks in ROM (DPBs) +; Tables of memory that describe the characteristics +; of discs on our system. There is one DPB for each +; disc type + +; ---------------------------------------- +; Block size | No of sectors | BSH | BLM | +; ---------------------------------------- +; 1K | 8 | 3 | 7 | +; 2K | 16 | 4 | 15 | +; 4K | 32 | 5 | 31 | +; 8K | 64 | 6 | 63 | +; 16K | 128 | 7 | 127 | +; ---------------------------------------- + +; ------------------------------------- +; Block size| Extent mask (EXM) | +; | Small disk | Large disk | +; ------------------------------------- +; 2K | 1 | 0 | +; 4K | 3 | 1 | +; 8K | 7 | 3 | +; 16K | 15 | 7 | +; ------------------------------------- +; CKS - number of dir sectors to check before write, 0 for HDD + +; For RAM-Disk 128k +dpb_ram: + DW 0010h ; SPT Sector (128b) per track (16d) + DB 03h ; BSH 1k + DB 07h ; BLM 1k; Allocation block size = (BLM + 1) * 128 = 1k + DB 00h ; EXM extent mask + DW 191 ; DSM Disk size blocks - 1 + DW 31 ; DRM Dir elements - 1 + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0008h ; CKS checksum vector size (8 sectors=1k) + DW 0000h ; OFF (tracks reserved for system) + +dpb_empty: + DS 15, 0xff + +; For FLOPPY 720k +dpb_flop_720k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 00h ; EXM extent mask + DW 359 ; DSM Disk size blocks - 1 (359d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +; For FLOPPY 360k +dpb_flop_360k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 01h ; EXM extent mask + DW 179 ; DSM Disk size blocks - 1 (179d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +bios_ini_vals: + DB 0xaa, 0xaa, 0, 0xff, 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +dph_disk_a: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + ; 0xda00 + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_A_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_A ; Check Vector pointer + DW CPM_VARS.AL_MAP_A ; Allocation map pointer + +; Disk B Floppy +dph_disk_b: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_B_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_B ; Check Vector pointer + DW CPM_VARS.AL_MAP_B ; Allocation map pointer + +; Disk C Floppy +dph_disk_c: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_C_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + + + +res_data: ; 0xda28 + DB 1,8,6,4,2,9,7,5,3 +DPB_END EQU $ + DB 0x0e, 3 + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT bios_wboot = 0xd6a8 + ASSERT set_curr_dpb = 0xd722 + ASSERT sel_disk = 0xd781 + ASSERT home = 0xd7a7 + ASSERT ram_disk_calc_addr = 0xd7eb + ASSERT write_phys_op = 0xd81e + ASSERT base_read_write = 0xd88a + ASSERT calc_sec_addr_in_bfr = 0xd8e4 + ASSERT slicer_save_buffer = 0xd928 + ASSERT print_strz = 0xd95e + ASSERT msg_hello = 0xd96b + ASSERT dpb_ram = 0xd9af + ASSERT dph_disk_a = 0xd9f8 + ASSERT res_data = 0xda28 + +FILLER: + DS FILL_SIZE, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_2e4a7b71/ccp_ram.asm b/CPM_v2.2_r8_2e4a7b71/ccp_ram.asm new file mode 100644 index 0000000..a2ecada --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/ccp_ram.asm @@ -0,0 +1,1525 @@ +; ====================================================== +; Ocean-240.2 +; CP/M V2.2 R8. +; 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 + +ccp_chg_drive: ; change drive flag, 0 - no change + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + + +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 |" + + ; Check integrity + ASSERT ccp_cmdline_back = 0xb986 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/ccp_rom.asm b/CPM_v2.2_r8_2e4a7b71/ccp_rom.asm new file mode 100644 index 0000000..b859801 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/ccp_rom.asm @@ -0,0 +1,901 @@ +; ======================================================= +; 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_cold_start + +; -------------------------------------------------- +; 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, (bios_var3) + 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, 0x4 + LD B, 0x0 + +.next_d: + LD E, 0x4 + +.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, 35 + JP jp_bdos_enter + +; ------------------------------------------------------- +; Read data from Tape +; ------------------------------------------------------- +ccp_read: + CALL BIOS.const_f + OR A + JP Z, .no_key ; chk key pressed + CALL BIOS.conin_f + JP ccp_read +.no_key: + CALL CCP_RAM.ccp_bdos_drv_allreset + CALL CCP_RAM.ccp_out_crlf + + ; wait tape block header + LD A, 100 + CALL BIOS.tape_wait_f + OR A + JP NZ, .key_pressed1 + + ; try to load header 8 times + LD E, 8 +.load8_cont: + LD HL, dma_buffer + CALL BIOS.tape_read_f + CP 0x4 + JP Z, .key_pressed1 + OR A + JP Z, .hdr_id_chk + DEC E + JP NZ, .load8_cont + JP .cp_load_err_what + + ; Check blk ID, will be 0x8000 +.hdr_id_chk: + LD A, B + CP 0x80 + JP NZ, .load8_cont + LD A, C + OR A + JP NZ, .load8_cont + ; set end of file name strZ + XOR A + LD (dma_buffer+12), A + LD DE, dma_buffer + LD HL, CCP_RAM.ccp_inp_line ;= 2020h + LD (CCP_RAM.ccp_inp_line_addr), HL ;= B208h + +.cp_name_char: + LD A, (DE) + OR A + JP Z, .name_copied + LD (HL), A ;= 2020h + INC HL + INC DE + JP .cp_name_char +.name_copied: + LD (HL), 0x0 ; mark end of strZ + CALL CCP_RAM.ccp_cv_first_to_fcb + JP NZ, CCP_RAM.print_syn_err + + ; Output file name to screen + LD HL, CCP_RAM.ccp_current_fcb_fn + LD B, 11 + +.name_output: + LD A, (HL) + AND 01111111b ; 7bit ASCII + LD C, A + CALL BIOS.conout_f + INC HL + DEC B + JP NZ, .name_output + ; Out ' ' + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL CCP_RAM.ccp_open_cur_fcb + JP Z, .recreate_f + LD HL,msg_ex ;= "*EX* " + CALL ccp_out_str_z + LD HL, CCP_RAM.ccp_current_fcb_fn+8 + ; '$$$' at the end of buffer + LD A, '$' + LD (HL), A + INC HL + LD (HL), A + INC HL + LD (HL), A + + ; delete file if exists and create new +.recreate_f: + LD DE, CCP_RAM.ccp_current_fcb + PUSH DE + CALL CCP_RAM.ccp_bdos_era_file + POP DE + CALL CCP_RAM.ccp_bdos_make_f + JP Z, CCP_RAM.ccp_no_space ; out NO SPACE if err + XOR A + LD (CCP_RAM.ccp_current_fcb_fn+31), A + + ; Load block with ID=1 + LD DE, 1 +.fin_load: + LD HL, dma_buffer + PUSH DE + +.blk_ld_rep: + CALL BIOS.tape_read_f + CP 4 + JP Z, .key_pressed2 + OR A + JP Z, .blk_id_chk + LD A, (CCP_RAM.ccp_current_fcb_fn+31) + AND 0x7f + JP NZ, .cp_read_cs + +.blk_id_chk: + LD A, C + OR B + JP Z, .blk_ld_rep + + ; Check id for 0xFFFF - last blk + LD A, C + AND B + CP 0xff + JP Z, .blk_id_last + ; Check ID for DE + LD A, C + CP E + JP NZ, .blk_ne_id + LD A, B + CP D + JP NZ, .blk_ne_id + ; Ok, write blk to disk + LD DE, dma_buffer + CALL CCP_RAM.ccp_bdos_dma_set + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_f_write + POP DE + JP NZ,ccp_del_f_no_space + INC DE + JP .fin_load + +.cp_load_err_what: + LD HL,msg_what ;= "WHAT?\a" + CALL ccp_out_str_z + JP .load_restart + +.cp_read_cs: + POP BC + LD HL, msg_cs ;= "\a", 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 diff --git a/CPM_v2.2_r8_2e4a7b71/cpm.asm b/CPM_v2.2_r8_2e4a7b71/cpm.asm new file mode 100644 index 0000000..7f3d1c9 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/cpm.asm @@ -0,0 +1,44 @@ +; ====================================================== +; Ocean-240.2 +; CP/M Combine file. Includes all sources to build +; ROM 0xC000 +; +; Disassembled by Romych 2025-02-12 +; ====================================================== + + DEFINE BUILD_ROM + + DEVICE NOSLOT64K + +; +; |-----------|---------------|-----------|--------------------------------------| +; | OFFSET | SIZE | Module | Memory Address | +; |-----------|---------------|-----------|--------------------------------------| +; | 0x0000 | 2048 (0x800) | CCP_RAM | 0xC000..0xC7FF -> RAM 0xB200..0xB5FF | +; | 0x0800 | 3584 (0xE00) | BDOS | 0xC800.. | +; | 0x1600 | 1024 (0x400) | BIOS | 0xD600..D9FF | +; | 0x1B00 | 1280 (0x500) | CCP_ROM | 0xDB00..DFFF | +; |-----------|---------------|-----------|--------------------------------------| +; + + DISPLAY "| Module | Offset | Size | Free |" + DISPLAY "|-------------|---------|--------|--------|" + + + OUTPUT cpm-C000.bin + ;LABELSLIST "cpm.labels" + ;CSPECTMAP "cpm.map" + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + OUTPUT variables.bin + INCLUDE "cpm_vars.inc" + OUTEND + + +END \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/cpm_vars.inc b/CPM_v2.2_r8_2e4a7b71/cpm_vars.inc new file mode 100644 index 0000000..ea33312 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/cpm_vars.inc @@ -0,0 +1,219 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM_VARS - BIOS + BDOS + CPP variables +; RAM Range: 0xBA09-0xBF00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _CPM_VARS + DEFINE _CPM_VARS + + MODULE CPM_VARS + ORG 0xBA09 + +cpm_vars_start EQU $ +; Filled by zeroes by BIOS at cold boot +; until ccp_vars_end + +; Output disabel if non zero +bdos_no_outflag DB 0 +bdos_strtcol DB 0 +bdos_column DB 0 + +; Copy output to printer in non zero +bdos_prnflag DB 0 +bdos_kbchar DB 0 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DW 0 ; 0xba0e + +; BDOS Stack + DS 48, 0 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DB 0x00 ; 0xba40 + +; current disk number +bdos_curdsk DB 0x00 + +; reg DE (pointer) value to call BDOS fn +bdos_info DW 0x0000 ; 0xba42 + +; Address value to return +bdos_aret DW 0x0000 ; 0xba44 +; Empty FCB +bdos_efcb DB 0x00 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DW 0x0000 ; 0xba47 +; Drive login vector +bdos_dlog DW 0x0000 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DW 0x0000 ; 0xba4b = 0x80 at init +bdos_cdrmax DW 0x0000 ; 0xba4d +; Current track +bdos_curtrk DW 0x0000 ; 0xba4f +; Current record (sector) +bdos_currec DW 0x0000 ; 0xba51 +; Address of user buffer +bdos_buffa DW 0x0000 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DW 0x0000 ; 0xba55 +; Calculated checksum +bdos_cksum DW 0x0000 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DW 0x0000 ; 0xba59 +; Sectors per track +dbos_sectpt DW 0x0000 ; 0xba5b +bdos_blkshf DB 0x00 ; 0xba5d +bdos_blmsk DB 0x00 ; 0xba5e +bdos_extmsk DB 0x00 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DW 0x0000 ; 0xba60 +bdos_dirmax DW 0x0000 ; 0xba62 +; Size of directory +bdos_dirblk DW 0x0000 ; 0xba64 +bdos_alloc1 DW 0x0000 ; 0xba66 +; First track offset +bdos_offset DW 0x0000 ; 0xba68 +; Disk traslation vector address +bdos_tranv DW 0x0000 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DB 0x00 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DB 0x00 ; 0xba6d +bdos_dirloc DB 0x00 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DB 0x00 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DB 0x00 ; 0xba70 +bdos_dminx DB 0x00 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DB 0x00 ; 0xba72 +; Address of buffer where to search +bdos_searcha DW 0x0000 ; 0xba73 + DW 0x0000 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DB 0x00 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DB 0x00 ; 0xba78 +bdos_olddsk DB 0x00 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DB 0x00 ; 0xba7a +; Number of records in extent +bdos_rcount DB 0x00 ; 0xba7b +bdos_extval DB 0x00 ; 0xba7c +; Record number to read +bdos_vrecord DW 0x0000 ; 0xba7d +; Record to access +bdos_arecord DW 0x0000 ; 0xba7f +bdos_arecord1 DW 0x0000 ; 0xba81 +bdos_dptr DB 0x00 ; 0xba83 +bdos_dcnt DW 0x00 ; 0xba84 +bdos_drec DS 20, 0x00 ; 0xba86 +tmp_dir_total DW 0x0000 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DW 0x0000 ; 0xba9c + DS 22, 0 +ccp_safe_stack + DW 0x0000 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_fcb +ccp_fcb_dr DB 0 ; 0xbab6 drive letter to write file +ccp_fcb_fn DS 8,0 ; 0xbab7 file name +ccp_fcb_ft DS 3,0 ; file type +ccp_fcb_ext DB 0 ; extent +ccp_fcb_s1 DB 0 +ccp_fcb_s2 DB 0 ; extent hi bits +ccp_fcb_rc DB 0 +ccp_fcb_al DS 16, 0 ; Second half of FCB +ccp_fcb_cr DB 0x00 ; Current record +ccp_fcb_rn DB 0x00 ; Random access record number + DW 0x00 + DW 0x00 + DW 0x00 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 ; 0xbaee +DPH2_RAM DS 16, 0 ; 0xbafe +tmp_buff9 DS 9, 0 +DPB_A_RAM DS 15, 0 ; 0xbb17 +DPB_B_RAM DS 15, 0 ; 0xbb26 +DPB_C_RAM DS 15, 0 ; 0xbb35 + +; -------------------------------------------------- +cur_disk DB 0x00 ; 0xbb44 +dma_addr DW 0x0000 ; 0xbb46 +curr_track DB 0x00 ; 0xbb47 +curr_sec DB 0x00 ; 0xbb48 +slicer_disk DB 0x00 ; 0xbb49 +slicer_track DB 0x00 ; 0xbb4a +slicer_real_sector DB 0x00 ; 0xbb4b +tmp_slicer_real_sector DB 0x00 ; 0xbb4c +slicer_has_data DB 0x00 ; 0xbb4d +slicer_need_save DB 0x00 ; 0xbb4e +slicer_uninited_count DB 0x00 ; 0xbb4f +slicer_uninited_disk DB 0x00 ; 0xbb42 +slicer_uninited_track DB 0x00 ; 0xbb43 +slicer_uninited_sector_128 DB 0x00 ; 0xbb44 +tmp_slicer_result DB 0x00 ; 0xbb45 +tmp_slicer_can_read DB 0x00 ; 0xbb46 +tmp_slicer_operation DB 0x00 ; 0xbb47 +tmp_slicer_flush DB 0x00 ; 0xbb48 + +; Directory buffer used by BDOS for directory operations +dir_buffer DS DIR_BUFF_SIZE, 0 ; 0xbb49 + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31, 0 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16, 0 ; 0xbbf6 + +; Allocation map for drive B +AL_MAP_B DS 45, 0 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 32, 0 ; 0xbc33 + +; Allocation map for drive C +AL_MAP_C DS 45, 0 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 32, 0 ; 0xbc80 + DS 96, 0 + +slicer_buffer DS 512, 0 ; 0xbd00 + +cpm_vars_end EQU $ + +ccp_vars_size EQU ccp_vars_end-cpm_vars_start +cpm_vars_size EQU cpm_vars_end-cpm_vars_start + + ; check integrity of original ROM + ASSERT tmp_dir_total = 0xba9a + ASSERT saved_stack_ptr = 0xba9c + ASSERT ccp_fcb_fn = 0xbab7 + ASSERT DPH_RAM = 0xbade + ASSERT cur_disk = 0xbb44 + ASSERT slicer_uninited_count = 0xbb4f + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + DISPLAY "cur_disk:",/H,cur_disk + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r8_2e4a7b71/equates.inc b/CPM_v2.2_r8_2e4a7b71/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/io.inc b/CPM_v2.2_r8_2e4a7b71/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/mon_entries.inc b/CPM_v2.2_r8_2e4a7b71/mon_entries.inc new file mode 100644 index 0000000..b607096 --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON + DEFINE _MON + + MODULE MON + +mon_init EQU 0xe000 +mon_cold_start EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mon_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_2e4a7b71/ram.inc b/CPM_v2.2_r8_2e4a7b71/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/CPM_v2.2_r8_2e4a7b71/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/.gitignore b/CPM_v2.2_r8_bc0695e4/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/CPM_v2.2_r8_bc0695e4/.vscode/extensions.json b/CPM_v2.2_r8_bc0695e4/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/CPM_v2.2_r8_bc0695e4/.vscode/tasks.json b/CPM_v2.2_r8_bc0695e4/.vscode/tasks.json new file mode 100644 index 0000000..f71ba83 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make CPM (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=cpm.sld", + "--sym=cpm.labels", + "--lstlab=sort", + "--lst=cpm.lst", + "--raw=cpm.obj", + "--fullpath", + "cpm.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/BIN/CPM_REL_8_HEXREAD.BIN b/CPM_v2.2_r8_bc0695e4/BIN/CPM_REL_8_HEXREAD.BIN new file mode 100644 index 0000000..88deb5d Binary files /dev/null and b/CPM_v2.2_r8_bc0695e4/BIN/CPM_REL_8_HEXREAD.BIN differ diff --git a/CPM_v2.2_r8_bc0695e4/README.md b/CPM_v2.2_r8_bc0695e4/README.md new file mode 100644 index 0000000..ba194e1 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/README.md @@ -0,0 +1,38 @@ +# Ocean-240.2 ROM Sources CP/M (V2.2) REL.8 checksum bc0695e4 + +Source codes of CP/M (V2.2) REL.8 version for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +* READ command replaced with Intel HEX loader by **tnt23** +* 192k RAM drive + +## Compile + + sjasmplus --sld=cpm.sld --sym=cpm.labels --raw=cpm.obj --fullpath cpm.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). + +## Convert binary file to Intel HEX + + srec_cat file_name.COM -binary -offset 0x100 -output file_name.hex -Intel + +## Send HEX from Linux console + +It is assumed that there is a USB-RS232 (USB-TTL) adapter on the /dev/ttyUSB0. + +1) At Ocean's CP/M command line: + + A>READ + +2) At Linux terminal, configure tty for 4800,8N2: + + stty -F /dev/ttyUSB0 4800 cs8 -cstopb + +3) At Linux terminal, send file: + + cat okeah.hex > /dev/ttyUSB0 + + +srec_cat - Utility from **srecord** package - collection of tools for manipulating EPROM load files. + +[Forum topic](https://zx-pk.ru/threads/35390-zagruzka-hex-fajlov-direktivoj-l-monitora.html) diff --git a/CPM_v2.2_r8_bc0695e4/bdos.asm b/CPM_v2.2_r8_bc0695e4/bdos.asm new file mode 100644 index 0000000..3cae2b1 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/bdos.asm @@ -0,0 +1,3041 @@ +; ======================================================= +; 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 + +; ------------------------------------------------------- +; 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 diff --git a/CPM_v2.2_r8_bc0695e4/bdos.inc b/CPM_v2.2_r8_bc0695e4/bdos.inc new file mode 100644 index 0000000..c5ae655 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/bdos.inc @@ -0,0 +1,54 @@ +; ======================================================= +; Ocean-240.2 +; CP/M v2.2. Equates to call BDOS functions +; +; Created by Romych 2026-02-16 +; ======================================================= + + + IFNDEF _BDOS_INC + DEFINE _BDOS_INC + +P_TERMCPM EQU 0 ; System Reset +C_READ EQU 1 ; Console input +C_WRITE EQU 2 ; Console output +A_READ EQU 3 ; Auxiliary (Reader) input +A_WRITE EQU 4 ; Auxiliary (Punch) output +L_WRITE EQU 5 ; Printer output +C_RAWIO EQU 6 ; Direct console I/O +A_STATIN EQU 7 ; Auxiliary Input status +A_STATOUT EQU 8 ; Auxiliary Output status +C_WRITESTR EQU 9 ; Output string +C_READSTR EQU 10 ; Buffered console input +C_STAT EQU 11 ; Console status +S_BDOSVER EQU 12 ; Return version number +DRV_ALLRESET EQU 13 ; Reset discs +DRV_SET EQU 14 ; Select disc +F_OPEN EQU 15 ; Open file +F_CLOSE EQU 16 ; Close file +F_SFIRST EQU 17 ; search for first +F_SNEXT EQU 18 ; search for next +F_DELETE EQU 19 ; delete file +F_READ EQU 20 ; read next record +F_WRITE EQU 21 ; write next record +F_MAKE EQU 22 ; create file +F_RENAME EQU 23 ; Rename file +DRV_LOGINVEC EQU 24 ; Return bitmap of logged-in drives +DRV_GET EQU 25 ; Return current drive +F_DMAOFF EQU 26 ; Set DMA address +DRV_ALLOCVEC EQU 27 ; Return address of allocation map +DRV_SETRO EQU 28 ; Software write-protect current disc +DRV_ROVEC EQU 29 ; Return bitmap of read-only drives +F_ATTRIB EQU 30 ; set file attributes +DRV_DPB EQU 31 ; get DPB address +F_USERNUM EQU 32 ; get/set user number +F_READRAND EQU 33 ; Random access read record +F_WRITERAND EQU 34 ; Random access write record +F_SIZE EQU 35 ; Compute file size +F_RANDREC EQU 36 ; Update random access pointer +DRV_RESET EQU 37 ; Selectively reset disc drives +DRV_ACCESS EQU 38 ; Access drives +DRV_FREE EQU 39 ; Free drive +F_WRITEZF EQU 40 ; Write random with zero fill + + ENDIF diff --git a/CPM_v2.2_r8_bc0695e4/bios.asm b/CPM_v2.2_r8_bc0695e4/bios.asm new file mode 100644 index 0000000..dedb8dc --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/bios.asm @@ -0,0 +1,823 @@ +; ======================================================= +; Ocean-240.2 +; +; CP/M BIOS +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "mon_entries.inc" + + IFNDEF BUILD_ROM + OUTPUT bios.bin + ENDIF + + MODULE BIOS + + ORG 0xD600 + +; ------------------------------------------------------- +; BIOS JUMP TABLE +; ------------------------------------------------------- +boot_f: + JP bios_boot +wboot_f: + JP bios_wboot + +; ------------------------------------------------------- +; console status to reg-a +; ------------------------------------------------------- +const_f: + JP MON.non_con_status + +; ------------------------------------------------------- +; console character to reg-a +; ------------------------------------------------------- +conin_f: + JP MON.mon_con_in + +; ------------------------------------------------------- +; console character from c to console out +; ------------------------------------------------------- +conout_f: + JP MON.mon_con_out + +; ------------------------------------------------------- +; list device out +; ------------------------------------------------------- +list_f: + JP MON.mon_char_print + +; ------------------------------------------------------- +; punch device out +; ------------------------------------------------------- +punch_f: + JP MON.mon_serial_out + +; ------------------------------------------------------- +; reader character in to reg-a +; ------------------------------------------------------- +reader_f: + JP MON.mon_serial_in + +; ------------------------------------------------------- +; move to home position, treat as track 00 seek +; ------------------------------------------------------- +home_f: + JP home + +; ------------------------------------------------------- +; select disk given by register c +; ------------------------------------------------------- +seldsk_f: + JP sel_disk +settrk_f: + JP set_trk +setsec_f: + JP set_sec + +; ------------------------------------------------------- +; Set DMA address from BC +; ------------------------------------------------------- +setdma_f: + JP set_dma +read_f: + JP read +write_f: + JP write +listst_f: + JP list_st +sectran_f: + JP sec_tran + +; ------------------------------------------------------- +; Reserved +; ------------------------------------------------------- + JP warm_boot + JP warm_boot + +; ------------------------------------------------------- +; Tape read +; ------------------------------------------------------- +tape_read_f: + JP MON.mon_tape_read + +; ------------------------------------------------------- +; Tape write +; ------------------------------------------------------- +tape_write_f: + JP MON.mon_tape_write + +; ------------------------------------------------------- +; Tape wait block +; ------------------------------------------------------- +tape_wait_f: + JP MON.mon_tape_wait + + JP warm_boot ; r8 + +disk_a_size DW 0x00C0 ; 192k disk A size +disk_b_size DW 0x02d0 ; 720 disk B size +disk_c_size DW 0x02d0 ; 720 disk C size +bios_var04 DB 0x50 +bios_var05 DB 0x50 + +; ------------------------------------------------------- +; cold start +; ------------------------------------------------------- +bios_boot: + LD HL, (bdos_ent_addr) ; 0xba06 + LD DE, 0x10000-CCP_RAM.bdos_enter_jump ; 0x45fa + ADD HL, DE ; 0xba06+0x45fa=10000 + LD A, H + OR L + JP Z, bios_signon + + ; >> r8 + LD HL, (bios_var0) ; if bios_var0 = 0xaaaa (initialized) + LD DE, 0x5556 + ADD HL, DE ; 0xaaaa+0x5556=0x10000 if initialized + LD A, H + OR L + JP Z, bios_signon ; if initialized, go to logon, skip init + ; << + + ; Init DMA buffer + LD HL, dma_buffer ; 0x8000 + LD B, DMA_BUFF_SIZE ; 0x80 +.init_dma_buff: + LD (HL), FILE_DELETED ; 0xE5 + INC HL + DEC B + JP NZ, .init_dma_buff + + ; Init RAM disk + LD HL, dma_buffer + LD DE, 0x0000 + LD B, 8 + +.init_ram_dsk: + PUSH BC + CALL MON.ram_disk_write + POP BC + INC DE + DEC B + JP NZ, .init_ram_dsk + + ; Init user to 0 and drive to 0 + XOR A + LD (cur_user_drv), A + + ; init bios variables + CALL bios_init_ilv ; r8 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + JP bios_wboot + +;bios_wboot: +; LD SP, bios_stack + +; r8 >> +bios_init_ilv: + LD HL, bios_var0 + LD DE, bios_ini_vals + LD C, 13 + CALL mov_dlhe_c + LD A, (bios_var05) ; 0x50 + LD (bios_var2), A + RET +; << + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: ; r8 + + LD HL, (bios_var0) + LD DE, 0x5556 ; 0xaaaa + 0x5556 = 0x10000 + ADD HL, DE + LD A, H + OR L + CALL NZ, bios_init_ilv ; init if not initialized before + LD SP, bios_stack + + ; Move CPP from 0xC000 to 0xB200 + LD HL, CCP_DST_ADDR + LD DE, CCP_SRC_ADDR + LD BC, CCP_SIZE +.move_ccp: + LD A, (DE) + LD (HL), A + INC DE + INC HL + DEC BC + LD A, B + OR C + JP NZ, .move_ccp + + ; Init variables with zeroes + LD HL, CPM_VARS.cpm_vars_start + LD BC, CPM_VARS.ccp_vars_size ; 213 + +.clr_cpm_vars: + LD (HL), 0x0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .clr_cpm_vars + + LD A, FILE_DELETED + LD (CPM_VARS.bdos_efcb), A ; mark empty FCB + LD A, low dma_buffer ; 0x80 + LD (CPM_VARS.bdos_dmaad), A ; 0x0080 + + ; Move DPH + DPB to RAM + LD HL, CPM_VARS.DPH_RAM + LD DE, dph_disk_a + LD BC, DPB_END-dph_disk_a ; 0x39 -> 57d + +.move_dph: + LD A, (DE) + LD (HL), A + INC HL + INC DE + DEC BC + LD A, B + OR C + JP NZ, .move_dph + + LD BC, dma_buffer ; DMA default buffer addr + CALL setdma_f + + ; Setup JP to Warm boot after CPU reset + LD A, JP_OPCODE + LD (warm_boot), A + LD HL, wboot_f + LD (warm_boot_addr), HL + + ; Setup JP to BDOS entrance + LD (jp_bdos_enter), A + LD HL, CCP_RAM.bdos_enter_jump + LD (bdos_ent_addr), HL + + LD HL, CPM_VARS.DPB_A_RAM + LD C, 0xf + LD DE, dpb_ram + LD A, (disk_a_size+1) + OR A + JP Z, set_curr_dpb + LD DE, dpb_empty + +set_curr_dpb: + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_B_RAM + LD C, 0xf + LD DE, dpb_flop_360k + LD A, (disk_b_size+1) + CP 0x1 + JP Z, .l1 + LD DE, dpb_flop_720k +.l1 + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_C_RAM + LD C, 0xf + LD A, (disk_c_size+1) + CP 0x2 + JP Z, .l2 + LD DE, dpb_flop_360k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_720k + JP .l3 +.l2 + LD DE, dpb_flop_720k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_360k +.l3 + CALL mov_dlhe_c + XOR A + LD (CPM_VARS.slicer_has_data),A + LD (CPM_VARS.slicer_uninited_count),A + LD A,(cur_user_drv) + LD C,A + ; go to CCP + JP CCP_DST_ADDR + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +mov_dlhe_c: + LD A,(DE) ; [DE]->[HL] + LD (HL),A + INC HL + INC DE + DEC C + JP NZ, mov_dlhe_c + RET + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD HL, 0x00 + LD A, C + CP CTRL_C + RET NC + + LD (CPM_VARS.cur_disk), A + OR A + JP Z, .get_dph_addr ; skip next for disk 0 - RAM disk + LD A, E ; selected disk map + AND 0x1 ; bit 0 is set if disk already selected + JP NZ, .get_dph_addr + ; Reset disk + LD (CPM_VARS.slicer_has_data), A + LD (CPM_VARS.slicer_uninited_count), A + ; calc DPH address of new drive +.get_dph_addr: + LD L, C + LD H, 0x0 + ADD HL, HL ; *2 + ADD HL, HL ; *4 + ADD HL, HL ; *8 + ADD HL, HL ; *16 (size of DBH) + LD DE, CPM_VARS.DPH_RAM + ADD HL, DE + RET + +; ------------------------------------------------------- +; move to track 00 +; ------------------------------------------------------- +home: + LD A, (CPM_VARS.cur_disk) + OR A + JP Z, .is_default + LD A, (CPM_VARS.slicer_need_save) + OR A + JP NZ, .is_default + LD (CPM_VARS.slicer_has_data), A ; set to 0, no data + +.is_default: + LD C, 0 ; set track to 0 + +; ------------------------------------------------------- +; set track address (0..76) for subsequent read/write +; ------------------------------------------------------- +set_trk: + LD HL, CPM_VARS.curr_track + LD (HL), C + RET + +; ------------------------------------------------------- +; set sector address (1,..., 26) for subsequent read/write +; ------------------------------------------------------- +set_sec: + LD HL, CPM_VARS.curr_sec + LD (HL), C + RET + +; ------------------------------------------------------- +; set subsequent dma address (initially 80h) +; ------------------------------------------------------- +set_dma: + LD L, C + LD H, B + LD (CPM_VARS.dma_addr), HL + RET + +sec_tran: + LD L, C + LD H, B + RET + +; ------------------------------------------------------- +; read track/sector to preset dma address +; ------------------------------------------------------- +read: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ, read_phys ; for physical disk use special routine + CALL ram_disk_calc_addr + CALL MON.ram_disk_read + XOR A + RET + +; ------------------------------------------------------- +; write track/sector from preset dma address +; ------------------------------------------------------- +write: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ,write_phys + CALL ram_disk_calc_addr + CALL MON.ram_disk_write + XOR A + RET + +; ------------------------------------------------------- +; Calculate address for current sector and track +; ------------------------------------------------------- +ram_disk_calc_addr: + LD HL, CPM_VARS.curr_track + ; HL = cur_track * 16 + LD L, (HL) + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ; DE = HL + cur_sec + EX DE, HL + LD HL, CPM_VARS.curr_sec + LD L, (HL) + LD H, 0x0 + ADD HL, DE + EX DE, HL + ; store address + LD HL, (CPM_VARS.dma_addr) + RET + +read_phys: + CALL read_phys_op + RET + +write_phys: + CALL write_phys_op + RET + +read_phys_op: + XOR A + ; reset counter + LD (CPM_VARS.slicer_uninited_count), A + LD A, 0x1 + LD (CPM_VARS.tmp_slicer_operation), A ; 0 - write; 1 - read + LD (CPM_VARS.tmp_slicer_can_read), A ; enable read fron disk + LD A, 0x2 + LD (CPM_VARS.tmp_slicer_flush), A ; disable flush data to disk + JP base_read_write + +write_phys_op: + XOR A + LD (CPM_VARS.tmp_slicer_operation), A + LD A, C + LD (CPM_VARS.tmp_slicer_flush), A + CP 0x2 + JP NZ, .mode_ne_2 + LD A, 0x10 ; 2048/128 + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_uninited_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_uninited_track), A + LD A, (CPM_VARS.curr_sec) + LD (CPM_VARS.slicer_uninited_sector_128), A +.mode_ne_2: + LD A, (CPM_VARS.slicer_uninited_count) + OR A + JP Z, slicer_read_write + DEC A + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_uninited_disk + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_uninited_track + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_sec) + LD HL, CPM_VARS.slicer_uninited_sector_128 + CP (HL) + JP NZ, slicer_read_write + INC (HL) + LD A, (HL) + CP 36 ; Sectors per track + JP C, .no_inc_track + LD (HL), 0x0 + LD A, (CPM_VARS.slicer_uninited_track) + INC A + LD (CPM_VARS.slicer_uninited_track), A + +.no_inc_track: + XOR A + LD (CPM_VARS.tmp_slicer_can_read), A + JP base_read_write + +; -------------------------------------------------- +slicer_read_write: + XOR A + LD (CPM_VARS.slicer_uninited_count), A + INC A + LD (CPM_VARS.tmp_slicer_can_read), A + +; -------------------------------------------------- +base_read_write: + XOR A + LD (CPM_VARS.tmp_slicer_result), A + LD A, (CPM_VARS.curr_sec) + OR A + RRA + OR A + RRA + LD (CPM_VARS.tmp_slicer_real_sector), A + LD HL, CPM_VARS.slicer_has_data + LD A, (HL) + LD (HL), 0x1 + OR A + JP Z, .no_data + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_disk + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_track + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD HL, CPM_VARS.slicer_real_sector + CP (HL) + JP Z,calc_sec_addr_in_bfr +.pos_diff: + LD A, (CPM_VARS.slicer_need_save) + OR A + CALL NZ, slicer_save_buffer ; save buffer if needed +.no_data: + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_track), A + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD (CPM_VARS.slicer_real_sector), A + LD A, (CPM_VARS.tmp_slicer_can_read) + OR A + CALL NZ,slicer_read_buffer + XOR A + LD (CPM_VARS.slicer_need_save), A + +; -------------------------------------------------- +calc_sec_addr_in_bfr: + LD A, (CPM_VARS.curr_sec) + AND 0x3 + LD L, A + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL ; *128 + LD DE, CPM_VARS.slicer_buffer + ADD HL, DE + EX DE, HL + LD HL, (CPM_VARS.dma_addr) + LD C, 0x80 + LD A, (CPM_VARS.tmp_slicer_operation) + OR A + JP NZ, .no_save + LD A, 0x1 + LD (CPM_VARS.slicer_need_save), A + EX DE, HL +.no_save: + LD A, (DE) + INC DE + LD (HL), A + INC HL + DEC C + JP NZ, .no_save + LD A, (CPM_VARS.tmp_slicer_flush) + CP 0x1 + LD A, (CPM_VARS.tmp_slicer_result) + RET NZ + OR A + RET NZ + XOR A + LD (CPM_VARS.slicer_need_save), A + CALL slicer_save_buffer + LD A, (CPM_VARS.tmp_slicer_result) + RET + +; -------------------------------------------------- +slicer_save_buffer: + CALL slicer_get_floppy_args + LD C, 0xA6 ; VG93 CMD + CALL MON.write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x86 ; VG93 CMD + CALL MON.read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_get_floppy_args: + LD HL, CPM_VARS.tmp_buff9 + LD A, (CPM_VARS.slicer_disk) + DEC A + JP Z, .non_interleave + LD HL, interleave_0 +.non_interleave + LD A, (CPM_VARS.slicer_real_sector) + ADD A, L + LD L, A + LD E, (HL) + LD A, (CPM_VARS.slicer_track) + LD D, A + LD HL, CPM_VARS.slicer_buffer + LD A, (CPM_VARS.slicer_disk) + RET + + +; ------------------------------------------------------- +; Print zerro ended string +; Inp: HL -> string +; ------------------------------------------------------- +print_strz: + LD A, (HL) + OR A + RET Z + LD C, A + PUSH HL + CALL conout_f + POP HL + INC HL + JP print_strz + +msg_hello: + DB ASCII_ESC, "60" ; Режим 32x18 60 + DB ASCII_ESC, "83" + DB ASCII_ESC, "5!%" + DB ASCII_ESC, "41" + DB ASCII_ESC, "1", 0x16, 0xe2, 0xe2, 0xfc, 0x01 + DB ASCII_ESC, "40" + DB ASCII_ESC, "1", 0x1e, 0xe6, 0xdb, 0xf8, 0x01 + DB ASCII_ESC, "43" + DB "OKEAH-240 CP/M (V2.2) REL.8'\r\n\n" + DB ASCII_ESC, "42", 0 + + +; -------------------------------------------------- +; Disk parameters blocks in ROM (DPBs) +; Tables of memory that describe the characteristics +; of discs on our system. There is one DPB for each +; disc type + +; ---------------------------------------- +; Block size | No of sectors | BSH | BLM | +; ---------------------------------------- +; 1K | 8 | 3 | 7 | +; 2K | 16 | 4 | 15 | +; 4K | 32 | 5 | 31 | +; 8K | 64 | 6 | 63 | +; 16K | 128 | 7 | 127 | +; ---------------------------------------- + +; ------------------------------------- +; Block size| Extent mask (EXM) | +; | Small disk | Large disk | +; ------------------------------------- +; 2K | 1 | 0 | +; 4K | 3 | 1 | +; 8K | 7 | 3 | +; 16K | 15 | 7 | +; ------------------------------------- +; CKS - number of dir sectors to check before write, 0 for HDD + +; For RAM-Disk 128k +dpb_ram: + DW 0010h ; SPT Sector (128b) per track (16d) + DB 03h ; BSH 1k + DB 07h ; BLM 1k; Allocation block size = (BLM + 1) * 128 = 1k + DB 00h ; EXM extent mask + DW 191 ; DSM Disk size blocks - 1 + DW 31 ; DRM Dir elements - 1 + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0008h ; CKS checksum vector size (8 sectors=1k) + DW 0000h ; OFF (tracks reserved for system) + +dpb_empty: + DS 15, 0xff + +; For FLOPPY 720k +dpb_flop_720k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 00h ; EXM extent mask + DW 359 ; DSM Disk size blocks - 1 (359d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +; For FLOPPY 360k +dpb_flop_360k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 01h ; EXM extent mask + DW 179 ; DSM Disk size blocks - 1 (179d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +bios_ini_vals: + DB 0xaa, 0xaa, 0, 0xff, 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +dph_disk_a: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + ; 0xda00 + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_A_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_A ; Check Vector pointer + DW CPM_VARS.AL_MAP_A ; Allocation map pointer + +; Disk B Floppy +dph_disk_b: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_B_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_B ; Check Vector pointer + DW CPM_VARS.AL_MAP_B ; Allocation map pointer + +; Disk C Floppy +dph_disk_c: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_C_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + + + +res_data: ; 0xda28 + DB 1,8,6,4,2,9,7,5,3 +DPB_END EQU $ + DB 0x0e, 3 + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT bios_wboot = 0xd6a8 + ASSERT set_curr_dpb = 0xd722 + ASSERT sel_disk = 0xd781 + ASSERT home = 0xd7a7 + ASSERT ram_disk_calc_addr = 0xd7eb + ASSERT write_phys_op = 0xd81e + ASSERT base_read_write = 0xd88a + ASSERT calc_sec_addr_in_bfr = 0xd8e4 + ASSERT slicer_save_buffer = 0xd928 + ASSERT print_strz = 0xd95e + ASSERT msg_hello = 0xd96b + ASSERT dpb_ram = 0xd9af + ASSERT dph_disk_a = 0xd9f8 + ASSERT res_data = 0xda28 + +FILLER: + DS FILL_SIZE, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_bc0695e4/ccp_ram.asm b/CPM_v2.2_r8_bc0695e4/ccp_ram.asm new file mode 100644 index 0000000..1d24e8b --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/ccp_ram.asm @@ -0,0 +1,1529 @@ +; ====================================================== +; Ocean-240.2 +; CP/M V2.2 R8. +; CCP (Console Command Processor) Resident part +; ORG C000 at RPM, moved to B200-BA09 +; +; Disassembled by Romych 2026-02-01 +; ====================================================== + + 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: +hex_length: + DB ASCII_SP, ASCII_SP + +msg_copyright: +hex_sectors: + DB " " +hex_buff: + + 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, C_WRITE + 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, DRV_ALLRESET + JP jp_bdos_enter + +; --------------------------------------------------- +; Call BDOS function 14 (DRV_SET) - Select disc +; Inp: A - disk +; --------------------------------------------------- +ccp_bdos_drv_set: + LD E, A + LD C, DRV_SET + 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, F_DELETE + 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, F_READ + 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, F_WRITE + JP ccp_bdos_enter_zf + +; --------------------------------------------------- +; Call BDOS function 22 (F_MAKE) - create file +; --------------------------------------------------- +ccp_bdos_make_f: + LD C, F_MAKE + JP ccp_call_bdos + +; --------------------------------------------------- +; Call BDOS function 23 (F_RENAME) - Rename file +; --------------------------------------------------- +ccp_bdos_rename_f: + LD C, F_RENAME + 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, F_USERNUM + 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, C_READSTR + 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, C_STAT + ; Call BDOS (C_STAT) - Console status + CALL jp_bdos_enter + OR A + RET Z ; ret if no character waiting + LD C, C_READ + ; 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, DRV_GET + 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, F_DMAOFF + 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 + +ccp_chg_drive: ; change drive flag, 0 - no change + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + + +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 |" + + ; Check integrity + ASSERT ccp_cmdline_back = 0xb986 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/ccp_rom.asm b/CPM_v2.2_r8_bc0695e4/ccp_rom.asm new file mode 100644 index 0000000..75e64ad --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/ccp_rom.asm @@ -0,0 +1,882 @@ +; ======================================================= +; 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, (bios_var3) + 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 diff --git a/CPM_v2.2_r8_bc0695e4/cpm.asm b/CPM_v2.2_r8_bc0695e4/cpm.asm new file mode 100644 index 0000000..6a473a0 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/cpm.asm @@ -0,0 +1,46 @@ +; ====================================================== +; Ocean-240.2 +; CP/M Combine file. Includes all sources to build +; ROM 0xC000 +; +; Disassembled by Romych 2025-02-12 +; ====================================================== + + DEFINE BUILD_ROM + + DEVICE NOSLOT64K + +; +; |-----------|---------------|-----------|--------------------------------------| +; | OFFSET | SIZE | Module | Memory Address | +; |-----------|---------------|-----------|--------------------------------------| +; | 0x0000 | 2048 (0x800) | CCP_RAM | 0xC000..0xC7FF -> RAM 0xB200..0xB5FF | +; | 0x0800 | 3584 (0xE00) | BDOS | 0xC800.. | +; | 0x1600 | 1024 (0x400) | BIOS | 0xD600..D9FF | +; | 0x1B00 | 1280 (0x500) | CCP_ROM | 0xDB00..DFFF | +; |-----------|---------------|-----------|--------------------------------------| +; + + DISPLAY "| Module | Offset | Size | Free |" + DISPLAY "|-------------|---------|--------|--------|" + + + OUTPUT cpm-C000.bin + ;LABELSLIST "cpm.labels" + ;CSPECTMAP "cpm.map" + INCLUDE "equates.inc" + INCLUDE "bdos.inc" + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + OUTPUT variables.bin + INCLUDE "cpm_vars.inc" + OUTEND + + +END \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/cpm_vars.inc b/CPM_v2.2_r8_bc0695e4/cpm_vars.inc new file mode 100644 index 0000000..ec00df2 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/cpm_vars.inc @@ -0,0 +1,219 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM_VARS - BIOS + BDOS + CPP variables +; RAM Range: 0xBA09-0xBF00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _CPM_VARS + DEFINE _CPM_VARS + + MODULE CPM_VARS + ORG 0xBA09 + +cpm_vars_start EQU $ +; Filled by zeroes by BIOS at cold boot +; until ccp_vars_end + +; Output disabel if non zero +bdos_no_outflag DB 0 +bdos_strtcol DB 0 +bdos_column DB 0 + +; Copy output to printer in non zero +bdos_prnflag DB 0 +bdos_kbchar DB 0 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DW 0 ; 0xba0e + +; BDOS Stack + DS 48, 0 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DB 0x00 ; 0xba40 + +; current disk number +bdos_curdsk DB 0x00 + +; reg DE (pointer) value to call BDOS fn +bdos_info DW 0x0000 ; 0xba42 + +; Address value to return +bdos_aret DW 0x0000 ; 0xba44 +; Empty FCB +bdos_efcb DB 0x00 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DW 0x0000 ; 0xba47 +; Drive login vector +bdos_dlog DW 0x0000 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DW 0x0000 ; 0xba4b = 0x80 at init +bdos_cdrmax DW 0x0000 ; 0xba4d +; Current track +bdos_curtrk DW 0x0000 ; 0xba4f +; Current record (sector) +bdos_currec DW 0x0000 ; 0xba51 +; Address of user buffer +bdos_buffa DW 0x0000 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DW 0x0000 ; 0xba55 +; Calculated checksum +bdos_cksum DW 0x0000 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DW 0x0000 ; 0xba59 +; Sectors per track +bdos_sectpt DW 0x0000 ; 0xba5b +bdos_blkshf DB 0x00 ; 0xba5d +bdos_blmsk DB 0x00 ; 0xba5e +bdos_extmsk DB 0x00 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DW 0x0000 ; 0xba60 +bdos_dirmax DW 0x0000 ; 0xba62 +; Size of directory +bdos_dirblk DW 0x0000 ; 0xba64 +bdos_alloc1 DW 0x0000 ; 0xba66 +; First track offset +bdos_offset DW 0x0000 ; 0xba68 +; Disk traslation vector address +bdos_tranv DW 0x0000 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DB 0x00 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DB 0x00 ; 0xba6d +bdos_dirloc DB 0x00 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DB 0x00 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DB 0x00 ; 0xba70 +bdos_dminx DB 0x00 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DB 0x00 ; 0xba72 +; Address of buffer where to search +bdos_searcha DW 0x0000 ; 0xba73 + DW 0x0000 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DB 0x00 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DB 0x00 ; 0xba78 +bdos_olddsk DB 0x00 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DB 0x00 ; 0xba7a +; Number of records in extent +bdos_rcount DB 0x00 ; 0xba7b +bdos_extval DB 0x00 ; 0xba7c +; Record number to read +bdos_vrecord DW 0x0000 ; 0xba7d +; Record to access +bdos_arecord DW 0x0000 ; 0xba7f +bdos_arecord1 DW 0x0000 ; 0xba81 +bdos_dptr DB 0x00 ; 0xba83 +bdos_dcnt DW 0x00 ; 0xba84 +bdos_drec DS 20, 0x00 ; 0xba86 +tmp_dir_total DW 0x0000 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DW 0x0000 ; 0xba9c + DS 22, 0 +ccp_safe_stack + DW 0x0000 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_fcb +ccp_fcb_dr DB 0 ; 0xbab6 drive letter to write file +ccp_fcb_fn DS 8,0 ; 0xbab7 file name +ccp_fcb_ft DS 3,0 ; file type +ccp_fcb_ext DB 0 ; extent +ccp_fcb_s1 DB 0 +ccp_fcb_s2 DB 0 ; extent hi bits +ccp_fcb_rc DB 0 +ccp_fcb_al DS 16, 0 ; Second half of FCB +ccp_fcb_cr DB 0x00 ; Current record +ccp_fcb_rn DB 0x00 ; Random access record number + DW 0x00 + DW 0x00 + DW 0x00 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 ; 0xbaee +DPH2_RAM DS 16, 0 ; 0xbafe +tmp_buff9 DS 9, 0 +DPB_A_RAM DS 15, 0 ; 0xbb17 +DPB_B_RAM DS 15, 0 ; 0xbb26 +DPB_C_RAM DS 15, 0 ; 0xbb35 + +; -------------------------------------------------- +cur_disk DB 0x00 ; 0xbb44 +dma_addr DW 0x0000 ; 0xbb46 +curr_track DB 0x00 ; 0xbb47 +curr_sec DB 0x00 ; 0xbb48 +slicer_disk DB 0x00 ; 0xbb49 +slicer_track DB 0x00 ; 0xbb4a +slicer_real_sector DB 0x00 ; 0xbb4b +tmp_slicer_real_sector DB 0x00 ; 0xbb4c +slicer_has_data DB 0x00 ; 0xbb4d +slicer_need_save DB 0x00 ; 0xbb4e +slicer_uninited_count DB 0x00 ; 0xbb4f +slicer_uninited_disk DB 0x00 ; 0xbb42 +slicer_uninited_track DB 0x00 ; 0xbb43 +slicer_uninited_sector_128 DB 0x00 ; 0xbb44 +tmp_slicer_result DB 0x00 ; 0xbb45 +tmp_slicer_can_read DB 0x00 ; 0xbb46 +tmp_slicer_operation DB 0x00 ; 0xbb47 +tmp_slicer_flush DB 0x00 ; 0xbb48 + +; Directory buffer used by BDOS for directory operations +dir_buffer DS DIR_BUFF_SIZE, 0 ; 0xbb49 + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31, 0 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16, 0 ; 0xbbf6 + +; Allocation map for drive B +AL_MAP_B DS 45, 0 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 32, 0 ; 0xbc33 + +; Allocation map for drive C +AL_MAP_C DS 45, 0 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 32, 0 ; 0xbc80 + DS 96, 0 + +slicer_buffer DS 512, 0 ; 0xbd00 + +cpm_vars_end EQU $ + +ccp_vars_size EQU ccp_vars_end-cpm_vars_start +cpm_vars_size EQU cpm_vars_end-cpm_vars_start + + ; check integrity of original ROM + ASSERT tmp_dir_total = 0xba9a + ASSERT saved_stack_ptr = 0xba9c + ASSERT ccp_fcb_fn = 0xbab7 + ASSERT DPH_RAM = 0xbade + ASSERT cur_disk = 0xbb44 + ASSERT slicer_uninited_count = 0xbb4f + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + ;DISPLAY "cur_disk:",/H,cur_disk + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r8_bc0695e4/equates.inc b/CPM_v2.2_r8_bc0695e4/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/io.inc b/CPM_v2.2_r8_bc0695e4/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/mon_entries.inc b/CPM_v2.2_r8_bc0695e4/mon_entries.inc new file mode 100644 index 0000000..3bb3149 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON + DEFINE _MON + + MODULE MON + +mon_init EQU 0xe000 +mon_hexb EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mon_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_bc0695e4/ram.inc b/CPM_v2.2_r8_bc0695e4/ram.inc new file mode 100644 index 0000000..38997e0 --- /dev/null +++ b/CPM_v2.2_r8_bc0695e4/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +;@RST1 EQU 0x0008 +;@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/.gitignore b/CPM_v2.2_r8_ddd05ed7/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/CPM_v2.2_r8_ddd05ed7/.vscode/extensions.json b/CPM_v2.2_r8_ddd05ed7/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/CPM_v2.2_r8_ddd05ed7/.vscode/tasks.json b/CPM_v2.2_r8_ddd05ed7/.vscode/tasks.json new file mode 100644 index 0000000..f71ba83 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make CPM (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=cpm.sld", + "--sym=cpm.labels", + "--lstlab=sort", + "--lst=cpm.lst", + "--raw=cpm.obj", + "--fullpath", + "cpm.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/BIN/CPM_REL_8_ddd05ed7.BIN b/CPM_v2.2_r8_ddd05ed7/BIN/CPM_REL_8_ddd05ed7.BIN new file mode 100644 index 0000000..ff95001 Binary files /dev/null and b/CPM_v2.2_r8_ddd05ed7/BIN/CPM_REL_8_ddd05ed7.BIN differ diff --git a/CPM_v2.2_r8_ddd05ed7/README.md b/CPM_v2.2_r8_ddd05ed7/README.md new file mode 100644 index 0000000..82c1da9 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/README.md @@ -0,0 +1,11 @@ +# Ocean-240.2 ROM Sources CP/M (V2.2) REL.8 checksum ddd05ed7 + +Source codes of CP/M (V2.2) REL.8 version for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +* 64k RAM Disk + +## Compile: + sjasmplus --sld=cpm.sld --sym=cpm.labels --raw=cpm.obj --fullpath cpm.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/CPM_v2.2_r8_ddd05ed7/bdos.asm b/CPM_v2.2_r8_ddd05ed7/bdos.asm new file mode 100644 index 0000000..3d21d38 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/bdos.asm @@ -0,0 +1,3041 @@ +; ======================================================= +; 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 + DW bdos_get_con_st ; fn11 + DW bdos_get_version ; fn12 + DW bdos_reset_disks ; fn13 + DW bdos_select_disk ; fn14 + DW bdos_open_file ; fn15 + DW bdos_close_file ; fn16 + DW bdos_search_first ; fn17 + DW bdos_search_next ; fn18 + DW bdos_rm_file ; fn19 + DW bdos_read_file ; fn20 + DW bdos_write_file ; fn21 + DW bdos_make_file ; fn22 + DW bdos_ren_file ; fn23 + DW bdos_get_login_vec ; fn24 + DW bdos_get_cur_drive ; fn25 + DW bdos_set_dma_addr ; fn26 + DW bdos_get_alloc_addr ; fn27 + DW bdos_set_ro ; fn28 + DW bdos_get_wr_protect ; fn29 + DW bdos_set_attr ; fn30 + DW bdos_get_dpb ; fn31 + DW bdos_set_user ; fn32 + DW bdos_rand_read ; fn33 + DW bdos_rand_write ; fn34 + DW bdos_compute_fs ; fn35 + DW bdos_set_random ; fn36 + DW bdos_reset_drives ; fn37 + DW bdos_not_impl ; fn38 (Access Drive) + DW bdos_not_impl ; fn39 (Free Drive) + DW bdos_rand_write_z ; fn40 + +; ------------------------------------------------------- +; 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.dbos_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.dbos_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.dbos_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 + +; ------------------------------------------------------- +; 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 diff --git a/CPM_v2.2_r8_ddd05ed7/bios.asm b/CPM_v2.2_r8_ddd05ed7/bios.asm new file mode 100644 index 0000000..e12ac0c --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/bios.asm @@ -0,0 +1,824 @@ +; ======================================================= +; Ocean-240.2 +; +; CP/M BIOS +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "mon_entries.inc" + + IFNDEF BUILD_ROM + OUTPUT bios.bin + ENDIF + + MODULE BIOS + + ORG 0xD600 + +; ------------------------------------------------------- +; BIOS JUMP TABLE +; ------------------------------------------------------- +boot_f: + JP bios_boot +wboot_f: + JP bios_wboot + +; ------------------------------------------------------- +; console status to reg-a +; ------------------------------------------------------- +const_f: + JP MON.non_con_status + +; ------------------------------------------------------- +; console character to reg-a +; ------------------------------------------------------- +conin_f: + JP MON.mon_con_in + +; ------------------------------------------------------- +; console character from c to console out +; ------------------------------------------------------- +conout_f: + JP MON.mon_con_out + +; ------------------------------------------------------- +; list device out +; ------------------------------------------------------- +list_f: + JP MON.mon_char_print + +; ------------------------------------------------------- +; punch device out +; ------------------------------------------------------- +punch_f: + JP MON.mon_serial_out + +; ------------------------------------------------------- +; reader character in to reg-a +; ------------------------------------------------------- +reader_f: + JP MON.mon_serial_in + +; ------------------------------------------------------- +; move to home position, treat as track 00 seek +; ------------------------------------------------------- +home_f: + JP home + +; ------------------------------------------------------- +; select disk given by register c +; ------------------------------------------------------- +seldsk_f: + JP sel_disk +settrk_f: + JP set_trk +setsec_f: + JP set_sec + +; ------------------------------------------------------- +; Set DMA address from BC +; ------------------------------------------------------- +setdma_f: + JP set_dma +read_f: + JP read +write_f: + JP write +listst_f: + JP list_st +sectran_f: + JP sec_tran + +; ------------------------------------------------------- +; Reserved +; ------------------------------------------------------- + JP warm_boot + JP warm_boot + +; ------------------------------------------------------- +; Tape read +; ------------------------------------------------------- +tape_read_f: + JP MON.mon_tape_read + +; ------------------------------------------------------- +; Tape write +; ------------------------------------------------------- +tape_write_f: + JP MON.mon_tape_write + +; ------------------------------------------------------- +; Tape wait block +; ------------------------------------------------------- +tape_wait_f: + JP MON.mon_tape_wait + + JP warm_boot ; + +disk_a_size DW 0x0040 ; 64k disk A size +disk_b_size DW 0x02d0 ; 720k disk B size +disk_c_size DW 0x02d0 ; 720k disk C size +bios_var04 DB 0x50 +bios_var05 DB 0x50 + +; ------------------------------------------------------- +; cold start +; ------------------------------------------------------- +bios_boot: + LD HL, (bdos_ent_addr) ; 0xba06 + LD DE, 0x10000-CCP_RAM.bdos_enter_jump ; 0x45fa + ADD HL, DE ; 0xba06+0x45fa=10000 + LD A, H + OR L + JP Z, bios_signon + + ; >> r8 + LD HL, (bios_var0) ; if bios_var0 = 0xaaaa (initialized) + LD DE, 0x5556 + ADD HL, DE ; 0xaaaa+0x5556=0x10000 if initialized + LD A, H + OR L + JP Z, bios_signon ; if initialized, go to logon, skip init + ; << + + ; Init DMA buffer + LD HL, dma_buffer ; 0x8000 + LD B, DMA_BUFF_SIZE ; 0x80 +.init_dma_buff: + LD (HL), FILE_DELETED ; 0xE5 + INC HL + DEC B + JP NZ, .init_dma_buff + + ; Init RAM disk + LD HL, dma_buffer + LD DE, 0x0000 + LD B, 8 + +.init_ram_dsk: + PUSH BC + CALL MON.ram_disk_write + POP BC + INC DE + DEC B + JP NZ, .init_ram_dsk + + ; Init user to 0 and drive to 0 + XOR A + LD (cur_user_drv), A + + ; init bios variables + CALL bios_init_ilv ; r8 + +bios_signon: + LD SP, bios_stack + ; Print CP/M hello message + LD HL, msg_hello + CALL print_strz + JP bios_wboot + +;bios_wboot: +; LD SP, bios_stack + +; r8 >> +bios_init_ilv: + LD HL, bios_var0 + LD DE, bios_ini_vals + LD C, 13 + CALL mov_dlhe_c + LD A, (bios_var05) ; 0x50 + LD (bios_var2), A + RET +; << + +; ------------------------------------------------------- +; BIOS Warm start entry +; ------------------------------------------------------- +bios_wboot: ; r8 + + LD HL, (bios_var0) + LD DE, 0x5556 ; 0xaaaa + 0x5556 = 0x10000 + ADD HL, DE + LD A, H + OR L + CALL NZ, bios_init_ilv ; init if not initialized before + LD SP, bios_stack + + ; Move CPP from 0xC000 to 0xB200 + LD HL, CCP_DST_ADDR + LD DE, CCP_SRC_ADDR + LD BC, CCP_SIZE +.move_ccp: + LD A, (DE) + LD (HL), A + INC DE + INC HL + DEC BC + LD A, B + OR C + JP NZ, .move_ccp + + ; Init variables with zeroes + LD HL, CPM_VARS.cpm_vars_start + LD BC, CPM_VARS.ccp_vars_size ; 213 + +.clr_cpm_vars: + LD (HL), 0x0 + INC HL + DEC BC + LD A, B + OR C + JP NZ, .clr_cpm_vars + + LD A, FILE_DELETED + LD (CPM_VARS.bdos_efcb), A ; mark empty FCB + LD A, low dma_buffer ; 0x80 + LD (CPM_VARS.bdos_dmaad), A ; 0x0080 + + ; Move DPH + DPB to RAM + LD HL, CPM_VARS.DPH_RAM + LD DE, dph_disk_a + LD BC, DPB_END-dph_disk_a ; 0x39 -> 57d + +.move_dph: + LD A, (DE) + LD (HL), A + INC HL + INC DE + DEC BC + LD A, B + OR C + JP NZ, .move_dph + + LD BC, dma_buffer ; DMA default buffer addr + CALL setdma_f + + ; Setup JP to Warm boot after CPU reset + LD A, JP_OPCODE + LD (warm_boot), A + LD HL, wboot_f + LD (warm_boot_addr), HL + + ; Setup JP to BDOS entrance + LD (jp_bdos_enter), A + LD HL, CCP_RAM.bdos_enter_jump + LD (bdos_ent_addr), HL + + LD HL, CPM_VARS.DPB_A_RAM + LD C, 0xf + LD DE, dpb_ram + LD A, (disk_a_size+1) + OR A + JP Z, set_curr_dpb + LD DE, dpb_empty + +set_curr_dpb: + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_B_RAM + LD C, 0xf + LD DE, dpb_flop_360k + LD A, (disk_b_size+1) + CP 0x1 + JP Z, .l1 + LD DE, dpb_flop_720k +.l1 + CALL mov_dlhe_c + LD HL, CPM_VARS.DPB_C_RAM + LD C, 0xf + LD A, (disk_c_size+1) + CP 0x2 + JP Z, .l2 + LD DE, dpb_flop_360k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_720k + JP .l3 +.l2 + LD DE, dpb_flop_720k + LD A, (bios_var3) + OR A + JP NZ, .l3 + LD DE, dpb_flop_360k +.l3 + CALL mov_dlhe_c + XOR A + LD (CPM_VARS.slicer_has_data),A + LD (CPM_VARS.slicer_uninited_count),A + LD A,(cur_user_drv) + LD C,A + ; go to CCP + JP CCP_DST_ADDR + +; ------------------------------------------------------- +; Move C bytes from [DE] to [HL] +; ------------------------------------------------------- +mov_dlhe_c: + LD A,(DE) ; [DE]->[HL] + LD (HL),A + INC HL + INC DE + DEC C + JP NZ, mov_dlhe_c + RET + +list_st: + XOR A + RET + +; ------------------------------------------------------- +; Select disk +; Inp: C - disk, +; E - active drive flag +; Out: HL -> DPH +; ------------------------------------------------------- +sel_disk: + LD HL, 0x00 + LD A, C + CP CTRL_C + RET NC + + LD (CPM_VARS.cur_disk), A + OR A + JP Z, .get_dph_addr ; skip next for disk 0 - RAM disk + LD A, E ; selected disk map + AND 0x1 ; bit 0 is set if disk already selected + JP NZ, .get_dph_addr + ; Reset disk + LD (CPM_VARS.slicer_has_data), A + LD (CPM_VARS.slicer_uninited_count), A + ; calc DPH address of new drive +.get_dph_addr: + LD L, C + LD H, 0x0 + ADD HL, HL ; *2 + ADD HL, HL ; *4 + ADD HL, HL ; *8 + ADD HL, HL ; *16 (size of DBH) + LD DE, CPM_VARS.DPH_RAM + ADD HL, DE + RET + +; ------------------------------------------------------- +; move to track 00 +; ------------------------------------------------------- +home: + LD A, (CPM_VARS.cur_disk) + OR A + JP Z, .is_default + LD A, (CPM_VARS.slicer_need_save) + OR A + JP NZ, .is_default + LD (CPM_VARS.slicer_has_data), A ; set to 0, no data + +.is_default: + LD C, 0 ; set track to 0 + +; ------------------------------------------------------- +; set track address (0..76) for subsequent read/write +; ------------------------------------------------------- +set_trk: + LD HL, CPM_VARS.curr_track + LD (HL), C + RET + +; ------------------------------------------------------- +; set sector address (1,..., 26) for subsequent read/write +; ------------------------------------------------------- +set_sec: + LD HL, CPM_VARS.curr_sec + LD (HL), C + RET + +; ------------------------------------------------------- +; set subsequent dma address (initially 80h) +; ------------------------------------------------------- +set_dma: + LD L, C + LD H, B + LD (CPM_VARS.dma_addr), HL + RET + +sec_tran: + LD L, C + LD H, B + RET + +; ------------------------------------------------------- +; read track/sector to preset dma address +; ------------------------------------------------------- +read: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ, read_phys ; for physical disk use special routine + CALL ram_disk_calc_addr + CALL MON.ram_disk_read + XOR A + RET + +; ------------------------------------------------------- +; write track/sector from preset dma address +; ------------------------------------------------------- +write: + LD A, (CPM_VARS.cur_disk) + OR A + JP NZ,write_phys + CALL ram_disk_calc_addr + CALL MON.ram_disk_write + XOR A + RET + +; ------------------------------------------------------- +; Calculate address for current sector and track +; ------------------------------------------------------- +ram_disk_calc_addr: + LD HL, CPM_VARS.curr_track + ; HL = cur_track * 16 + LD L, (HL) + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ; DE = HL + cur_sec + EX DE, HL + LD HL, CPM_VARS.curr_sec + LD L, (HL) + LD H, 0x0 + ADD HL, DE + EX DE, HL + ; store address + LD HL, (CPM_VARS.dma_addr) + RET + +read_phys: + CALL read_phys_op + RET + +write_phys: + CALL write_phys_op + RET + +read_phys_op: + XOR A + ; reset counter + LD (CPM_VARS.slicer_uninited_count), A + LD A, 0x1 + LD (CPM_VARS.tmp_slicer_operation), A ; 0 - write; 1 - read + LD (CPM_VARS.tmp_slicer_can_read), A ; enable read fron disk + LD A, 0x2 + LD (CPM_VARS.tmp_slicer_flush), A ; disable flush data to disk + JP base_read_write + +write_phys_op: + XOR A + LD (CPM_VARS.tmp_slicer_operation), A + LD A, C + LD (CPM_VARS.tmp_slicer_flush), A + CP 0x2 + JP NZ, .mode_ne_2 + LD A, 0x10 ; 2048/128 + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_uninited_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_uninited_track), A + LD A, (CPM_VARS.curr_sec) + LD (CPM_VARS.slicer_uninited_sector_128), A +.mode_ne_2: + LD A, (CPM_VARS.slicer_uninited_count) + OR A + JP Z, slicer_read_write + DEC A + LD (CPM_VARS.slicer_uninited_count), A + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_uninited_disk + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_uninited_track + CP (HL) + JP NZ, slicer_read_write + LD A, (CPM_VARS.curr_sec) + LD HL, CPM_VARS.slicer_uninited_sector_128 + CP (HL) + JP NZ, slicer_read_write + INC (HL) + LD A, (HL) + CP 36 ; Sectors per track + JP C, .no_inc_track + LD (HL), 0x0 + LD A, (CPM_VARS.slicer_uninited_track) + INC A + LD (CPM_VARS.slicer_uninited_track), A + +.no_inc_track: + XOR A + LD (CPM_VARS.tmp_slicer_can_read), A + JP base_read_write + +; -------------------------------------------------- +slicer_read_write: + XOR A + LD (CPM_VARS.slicer_uninited_count), A + INC A + LD (CPM_VARS.tmp_slicer_can_read), A + +; -------------------------------------------------- +base_read_write: + XOR A + LD (CPM_VARS.tmp_slicer_result), A + LD A, (CPM_VARS.curr_sec) + OR A + RRA + OR A + RRA + LD (CPM_VARS.tmp_slicer_real_sector), A + LD HL, CPM_VARS.slicer_has_data + LD A, (HL) + LD (HL), 0x1 + OR A + JP Z, .no_data + LD A, (CPM_VARS.cur_disk) + LD HL, CPM_VARS.slicer_disk + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.curr_track) + LD HL, CPM_VARS.slicer_track + CP (HL) + JP NZ, .pos_diff + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD HL, CPM_VARS.slicer_real_sector + CP (HL) + JP Z,calc_sec_addr_in_bfr +.pos_diff: + LD A, (CPM_VARS.slicer_need_save) + OR A + CALL NZ, slicer_save_buffer ; save buffer if needed +.no_data: + LD A, (CPM_VARS.cur_disk) + LD (CPM_VARS.slicer_disk), A + LD A, (CPM_VARS.curr_track) + LD (CPM_VARS.slicer_track), A + LD A, (CPM_VARS.tmp_slicer_real_sector) + LD (CPM_VARS.slicer_real_sector), A + LD A, (CPM_VARS.tmp_slicer_can_read) + OR A + CALL NZ,slicer_read_buffer + XOR A + LD (CPM_VARS.slicer_need_save), A + +; -------------------------------------------------- +calc_sec_addr_in_bfr: + LD A, (CPM_VARS.curr_sec) + AND 0x3 + LD L, A + LD H, 0x0 + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL + ADD HL, HL ; *128 + LD DE, CPM_VARS.slicer_buffer + ADD HL, DE + EX DE, HL + LD HL, (CPM_VARS.dma_addr) + LD C, 0x80 + LD A, (CPM_VARS.tmp_slicer_operation) + OR A + JP NZ, .no_save + LD A, 0x1 + LD (CPM_VARS.slicer_need_save), A + EX DE, HL +.no_save: + LD A, (DE) + INC DE + LD (HL), A + INC HL + DEC C + JP NZ, .no_save + LD A, (CPM_VARS.tmp_slicer_flush) + CP 0x1 + LD A, (CPM_VARS.tmp_slicer_result) + RET NZ + OR A + RET NZ + XOR A + LD (CPM_VARS.slicer_need_save), A + CALL slicer_save_buffer + LD A, (CPM_VARS.tmp_slicer_result) + RET + +; -------------------------------------------------- +slicer_save_buffer: + CALL slicer_get_floppy_args + LD C, 0xA6 ; VG93 CMD + CALL MON.write_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_read_buffer: + CALL slicer_get_floppy_args + LD C, 0x86 ; VG93 CMD + CALL MON.read_floppy + LD (CPM_VARS.tmp_slicer_result), A + RET + +; -------------------------------------------------- +slicer_get_floppy_args: + LD HL, CPM_VARS.tmp_buff9 + LD A, (CPM_VARS.slicer_disk) + DEC A + JP Z, .non_interleave + LD HL, interleave_0 +.non_interleave + LD A, (CPM_VARS.slicer_real_sector) + ADD A, L + LD L, A + LD E, (HL) + LD A, (CPM_VARS.slicer_track) + LD D, A + LD HL, CPM_VARS.slicer_buffer + LD A, (CPM_VARS.slicer_disk) + RET + + +; ------------------------------------------------------- +; Print zerro ended string +; Inp: HL -> string +; ------------------------------------------------------- +print_strz: + LD A, (HL) + OR A + RET Z + LD C, A + PUSH HL + CALL conout_f + POP HL + INC HL + JP print_strz + +msg_hello: + DB ASCII_ESC, "6", "0" ; Режим 32x25 60 + DB ASCII_ESC, "8", 3 + DB ASCII_ESC, "5% " + DB ASCII_ESC, "4", "2" + DB ASCII_ESC, "1", 6, 0xE1, 0xF6, 0xFE, 0x01 + DB ASCII_ESC, "40" + DB ASCII_ESC, "1", 10, 0xE5, 0xF2, 0xF9, 0x01 + DB ASCII_ESC, "4", "1" + DB ASCII_ESC, "5", 0x21, 0x27 + DB "OKEAH-240 CP/M (V2.2) REL.8\r\n\n", 0 + + + +; -------------------------------------------------- +; Disk parameters blocks in ROM (DPBs) +; Tables of memory that describe the characteristics +; of discs on our system. There is one DPB for each +; disc type + +; ---------------------------------------- +; Block size | No of sectors | BSH | BLM | +; ---------------------------------------- +; 1K | 8 | 3 | 7 | +; 2K | 16 | 4 | 15 | +; 4K | 32 | 5 | 31 | +; 8K | 64 | 6 | 63 | +; 16K | 128 | 7 | 127 | +; ---------------------------------------- + +; ------------------------------------- +; Block size| Extent mask (EXM) | +; | Small disk | Large disk | +; ------------------------------------- +; 2K | 1 | 0 | +; 4K | 3 | 1 | +; 8K | 7 | 3 | +; 16K | 15 | 7 | +; ------------------------------------- +; CKS - number of dir sectors to check before write, 0 for HDD + +; For RAM-Disk 64k +dpb_ram: + DW 0010h ; SPT Sector (128b) per track (16d) + DB 03h ; BSH 1k + DB 07h ; BLM 1k; Allocation block size = (BLM + 1) * 128 = 1k + DB 00h ; EXM extent mask + DW 63 ; DSM Disk size blocks - 1 + DW 31 ; DRM Dir elements - 1 + DB 10000000b ; AL0 Dir map byte 1 + DB 00000000b ; AL1 Dir map byte 2 + DW 0008h ; CKS checksum vector size (8 sectors=1k) + DW 0000h ; OFF (tracks reserved for system) + +dpb_empty: + DS 15, 0xff + +; For FLOPPY 720k +dpb_flop_720k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 00h ; EXM extent mask + DW 359 ; DSM Disk size blocks - 1 (359d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +; For FLOPPY 360k +dpb_flop_360k: + DW 0024h ; SPT Sector (128b) per track 36 * 128 = 18KB + DB 04h ; BSH 2k + DB 0Fh ; BLM 2k; Allocation block size = (BLM + 1) * 128 = 2k + DB 01h ; EXM extent mask + DW 179 ; DSM Disk size blocks - 1 (179d) + DW 127 ; DRM Directory entries - 1 (127d) + DB 11000000b ; AL0 Dir map byte 1 (2 dir blk) + DB 00000000b ; AL1 Dir map byte 2 + DW 32 ; CKS checksum vector size (32 sectors = 2k) + DW 0000h ; OFF (No of tracks reserved for system) + +bios_ini_vals: + DB 0xaa, 0xaa, 0, 0xff, 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; -------------------------------------------------- +; Disk parameters headers in ROM +; -------------------------------------------------- +; Disk A RAM +dph_disk_a: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + ; 0xda00 + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_A_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_A ; Check Vector pointer + DW CPM_VARS.AL_MAP_A ; Allocation map pointer + +; Disk B Floppy +dph_disk_b: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_B_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_B ; Check Vector pointer + DW CPM_VARS.AL_MAP_B ; Allocation map pointer + +; Disk C Floppy +dph_disk_c: + DW 00h ; Sector translate table pointer + DW 00h, 00h, 00h ; Scratchpad area + DW CPM_VARS.dir_buffer ; Directory buffer pointer + DW CPM_VARS.DPB_C_RAM ; DPB Pointer + DW CPM_VARS.CHK_VEC_C ; Check Vector pointer + DW CPM_VARS.AL_MAP_C ; Allocation map pointer + + + +res_data: ; 0xda28 + DB 1,8,6,4,2,9,7,5,3 +DPB_END EQU $ + DB 0x0e, 3 + + +; ------------------------------------------------------- +; Filler to align blocks in ROM +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xD600 +FILL_SIZE EQU 0x500-CODE_SIZE + + DISPLAY "| BIOS\t| ",/H,boot_f," | ",/H,CODE_SIZE," | ",/H,FILL_SIZE," |" + + ; Check integrity + ASSERT bios_wboot = 0xd6a8 + ASSERT set_curr_dpb = 0xd722 + ASSERT sel_disk = 0xd781 + ASSERT home = 0xd7a7 + ASSERT ram_disk_calc_addr = 0xd7eb + ASSERT write_phys_op = 0xd81e + ASSERT base_read_write = 0xd88a + ASSERT calc_sec_addr_in_bfr = 0xd8e4 + ASSERT slicer_save_buffer = 0xd928 + ASSERT print_strz = 0xd95e + ASSERT msg_hello = 0xd96b + ASSERT dpb_ram = 0xd9af + ASSERT dph_disk_a = 0xd9f8 + ASSERT res_data = 0xda28 + +FILLER: + DS FILL_SIZE, 0xff + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_ddd05ed7/ccp_ram.asm b/CPM_v2.2_r8_ddd05ed7/ccp_ram.asm new file mode 100644 index 0000000..962f044 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/ccp_ram.asm @@ -0,0 +1,1525 @@ +; ====================================================== +; Ocean-240.2 +; CP/M V2.2 R8. +; 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 0x1 ; DIFF + 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 + +ccp_chg_drive: ; change drive flag, 0 - no change + DB 0 +ccp_bytes_ctr: + DW 0 + ; reserved + DS 13, 0 + + +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 |" + + ; Check integrity + ASSERT ccp_cmdline_back = 0xb986 + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/ccp_rom.asm b/CPM_v2.2_r8_ddd05ed7/ccp_rom.asm new file mode 100644 index 0000000..ba34e8d --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/ccp_rom.asm @@ -0,0 +1,901 @@ +; ======================================================= +; 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_cold_start + +; -------------------------------------------------- +; 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, (bios_var3) + 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, 0x4 + LD B, 0x0 + +.next_d: + LD E, 0x4 + +.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, 35 + JP jp_bdos_enter + +; ------------------------------------------------------- +; Read data from Tape +; ------------------------------------------------------- +ccp_read: + CALL BIOS.const_f + OR A + JP Z, .no_key ; chk key pressed + CALL BIOS.conin_f + JP ccp_read +.no_key: + CALL CCP_RAM.ccp_bdos_drv_allreset + CALL CCP_RAM.ccp_out_crlf + + ; wait tape block header + LD A, 100 + CALL BIOS.tape_wait_f + OR A + JP NZ, .key_pressed1 + + ; try to load header 8 times + LD E, 8 +.load8_cont: + LD HL, dma_buffer + CALL BIOS.tape_read_f + CP 0x4 + JP Z, .key_pressed1 + OR A + JP Z, .hdr_id_chk + DEC E + JP NZ, .load8_cont + JP .cp_load_err_what + + ; Check blk ID, will be 0x8000 +.hdr_id_chk: + LD A, B + CP 0x80 + JP NZ, .load8_cont + LD A, C + OR A + JP NZ, .load8_cont + ; set end of file name strZ + XOR A + LD (dma_buffer+12), A + LD DE, dma_buffer + LD HL, CCP_RAM.ccp_inp_line ;= 2020h + LD (CCP_RAM.ccp_inp_line_addr), HL ;= B208h + +.cp_name_char: + LD A, (DE) + OR A + JP Z, .name_copied + LD (HL), A ;= 2020h + INC HL + INC DE + JP .cp_name_char +.name_copied: + LD (HL), 0x0 ; mark end of strZ + CALL CCP_RAM.ccp_cv_first_to_fcb + JP NZ, CCP_RAM.print_syn_err + + ; Output file name to screen + LD HL, CCP_RAM.ccp_current_fcb_fn + LD B, 11 + +.name_output: + LD A, (HL) + AND 01111111b ; 7bit ASCII + LD C, A + CALL BIOS.conout_f + INC HL + DEC B + JP NZ, .name_output + ; Out ' ' + LD C, ' ' + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL BIOS.conout_f + CALL CCP_RAM.ccp_open_cur_fcb + JP Z, .recreate_f + LD HL,msg_ex ;= "*EX* " + CALL ccp_out_str_z + LD HL, CCP_RAM.ccp_current_fcb_fn+8 + ; '$$$' at the end of buffer + LD A, '$' + LD (HL), A + INC HL + LD (HL), A + INC HL + LD (HL), A + + ; delete file if exists and create new +.recreate_f: + LD DE, CCP_RAM.ccp_current_fcb + PUSH DE + CALL CCP_RAM.ccp_bdos_era_file + POP DE + CALL CCP_RAM.ccp_bdos_make_f + JP Z, CCP_RAM.ccp_no_space ; out NO SPACE if err + XOR A + LD (CCP_RAM.ccp_current_fcb_fn+31), A + + ; Load block with ID=1 + LD DE, 1 +.fin_load: + LD HL, dma_buffer + PUSH DE + +.blk_ld_rep: + CALL BIOS.tape_read_f + CP 4 + JP Z, .key_pressed2 + OR A + JP Z, .blk_id_chk + LD A, (CCP_RAM.ccp_current_fcb_fn+31) + AND 0x7f + JP NZ, .cp_read_cs + +.blk_id_chk: + LD A, C + OR B + JP Z, .blk_ld_rep + + ; Check id for 0xFFFF - last blk + LD A, C + AND B + CP 0xff + JP Z, .blk_id_last + ; Check ID for DE + LD A, C + CP E + JP NZ, .blk_ne_id + LD A, B + CP D + JP NZ, .blk_ne_id + ; Ok, write blk to disk + LD DE, dma_buffer + CALL CCP_RAM.ccp_bdos_dma_set + LD DE, CCP_RAM.ccp_current_fcb + CALL CCP_RAM.ccp_bdos_f_write + POP DE + JP NZ,ccp_del_f_no_space + INC DE + JP .fin_load + +.cp_load_err_what: + LD HL,msg_what ;= "WHAT?\a" + CALL ccp_out_str_z + JP .load_restart + +.cp_read_cs: + POP BC + LD HL, msg_cs ;= "\a", 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, 1500 ; DIFF +.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, 0xff + ;DB 0xaa + + ENDMODULE + + IFNDEF BUILD_ROM + OUTEND + ENDIF diff --git a/CPM_v2.2_r8_ddd05ed7/cpm.asm b/CPM_v2.2_r8_ddd05ed7/cpm.asm new file mode 100644 index 0000000..7f3d1c9 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/cpm.asm @@ -0,0 +1,44 @@ +; ====================================================== +; Ocean-240.2 +; CP/M Combine file. Includes all sources to build +; ROM 0xC000 +; +; Disassembled by Romych 2025-02-12 +; ====================================================== + + DEFINE BUILD_ROM + + DEVICE NOSLOT64K + +; +; |-----------|---------------|-----------|--------------------------------------| +; | OFFSET | SIZE | Module | Memory Address | +; |-----------|---------------|-----------|--------------------------------------| +; | 0x0000 | 2048 (0x800) | CCP_RAM | 0xC000..0xC7FF -> RAM 0xB200..0xB5FF | +; | 0x0800 | 3584 (0xE00) | BDOS | 0xC800.. | +; | 0x1600 | 1024 (0x400) | BIOS | 0xD600..D9FF | +; | 0x1B00 | 1280 (0x500) | CCP_ROM | 0xDB00..DFFF | +; |-----------|---------------|-----------|--------------------------------------| +; + + DISPLAY "| Module | Offset | Size | Free |" + DISPLAY "|-------------|---------|--------|--------|" + + + OUTPUT cpm-C000.bin + ;LABELSLIST "cpm.labels" + ;CSPECTMAP "cpm.map" + + INCLUDE "ccp_ram.asm" + INCLUDE "bdos.asm" + INCLUDE "bios.asm" + INCLUDE "ccp_rom.asm" + + OUTEND + + OUTPUT variables.bin + INCLUDE "cpm_vars.inc" + OUTEND + + +END \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/cpm_vars.inc b/CPM_v2.2_r8_ddd05ed7/cpm_vars.inc new file mode 100644 index 0000000..ea33312 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/cpm_vars.inc @@ -0,0 +1,219 @@ +; ======================================================= +; Ocean-240.2 +; Module CPM_VARS - BIOS + BDOS + CPP variables +; RAM Range: 0xBA09-0xBF00 +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _CPM_VARS + DEFINE _CPM_VARS + + MODULE CPM_VARS + ORG 0xBA09 + +cpm_vars_start EQU $ +; Filled by zeroes by BIOS at cold boot +; until ccp_vars_end + +; Output disabel if non zero +bdos_no_outflag DB 0 +bdos_strtcol DB 0 +bdos_column DB 0 + +; Copy output to printer in non zero +bdos_prnflag DB 0 +bdos_kbchar DB 0 + +; Stored user stack pointer on enter bdos fn +bdos_usersp DW 0 ; 0xba0e + +; BDOS Stack + DS 48, 0 +bdos_stack EQU $ + +; ------------------------------------------------------- +; Common values shared between bdosi and bdos +; ------------------------------------------------------- + +; current user number 0..15 +bdos_userno DB 0x00 ; 0xba40 + +; current disk number +bdos_curdsk DB 0x00 + +; reg DE (pointer) value to call BDOS fn +bdos_info DW 0x0000 ; 0xba42 + +; Address value to return +bdos_aret DW 0x0000 ; 0xba44 +; Empty FCB +bdos_efcb DB 0x00 ; 0xba46 = 0xE5 at init +; Read-only disks flag +bdos_rodsk DW 0x0000 ; 0xba47 +; Drive login vector +bdos_dlog DW 0x0000 ; 0xba49 +; Address of DMA buffer +bdos_dmaad DW 0x0000 ; 0xba4b = 0x80 at init +bdos_cdrmax DW 0x0000 ; 0xba4d +; Current track +bdos_curtrk DW 0x0000 ; 0xba4f +; Current record (sector) +bdos_currec DW 0x0000 ; 0xba51 +; Address of user buffer +bdos_buffa DW 0x0000 ; 0xba53 + +; Address of cur disk DPB +bdos_dpbaddr DW 0x0000 ; 0xba55 +; Calculated checksum +bdos_cksum DW 0x0000 ; 0xba57 +; Address of cur disk allocation map +bdos_alloca DW 0x0000 ; 0xba59 +; Sectors per track +dbos_sectpt DW 0x0000 ; 0xba5b +bdos_blkshf DB 0x00 ; 0xba5d +bdos_blmsk DB 0x00 ; 0xba5e +bdos_extmsk DB 0x00 ; 0xba5f +; Max allocation number (disk size) +bdos_maxall DW 0x0000 ; 0xba60 +bdos_dirmax DW 0x0000 ; 0xba62 +; Size of directory +bdos_dirblk DW 0x0000 ; 0xba64 +bdos_alloc1 DW 0x0000 ; 0xba66 +; First track offset +bdos_offset DW 0x0000 ; 0xba68 +; Disk traslation vector address +bdos_tranv DW 0x0000 ; 0xba6a +; Open and closed flag 0xff - closed +bdos_fcb_closed DB 0x00 ; 0xba6c +; Read file modified flag. 0xff - not allow read unwritten +bdos_rfm DB 0x00 ; 0xba6d +bdos_dirloc DB 0x00 ; 0xba6e +; File access mode (0=random, 1=sequential, 2=special) +bdos_seqio DB 0x00 ; 0xba6f +; Reg E value to call BDOS fn +bdos_linfo DB 0x00 ; 0xba70 +bdos_dminx DB 0x00 ; 0xba71 +; Length in bytes to match when search +bdos_searchl DB 0x00 ; 0xba72 +; Address of buffer where to search +bdos_searcha DW 0x0000 ; 0xba73 + DW 0x0000 +; 0 - big disk (>255 blk), 0xff - small disk +bdos_small_disk DB 0x00 ; 0xba77 +; Drive selected (0xff) by user specified FCB flag +bdos_resel DB 0x00 ; 0xba78 +bdos_olddsk DB 0x00 ; 0xba79 +; Saved disk of user FCB +bdos_fcbdsk DB 0x00 ; 0xba7a +; Number of records in extent +bdos_rcount DB 0x00 ; 0xba7b +bdos_extval DB 0x00 ; 0xba7c +; Record number to read +bdos_vrecord DW 0x0000 ; 0xba7d +; Record to access +bdos_arecord DW 0x0000 ; 0xba7f +bdos_arecord1 DW 0x0000 ; 0xba81 +bdos_dptr DB 0x00 ; 0xba83 +bdos_dcnt DW 0x00 ; 0xba84 +bdos_drec DS 20, 0x00 ; 0xba86 +tmp_dir_total DW 0x0000 ; 0xba9a +; Save pointer at ccp entry, restore on exit +saved_stack_ptr DW 0x0000 ; 0xba9c + DS 22, 0 +ccp_safe_stack + DW 0x0000 ; 0xbab4 + +; -------------------------------------------------- +; FCB +; -------------------------------------------------- +ccp_fcb +ccp_fcb_dr DB 0 ; 0xbab6 drive letter to write file +ccp_fcb_fn DS 8,0 ; 0xbab7 file name +ccp_fcb_ft DS 3,0 ; file type +ccp_fcb_ext DB 0 ; extent +ccp_fcb_s1 DB 0 +ccp_fcb_s2 DB 0 ; extent hi bits +ccp_fcb_rc DB 0 +ccp_fcb_al DS 16, 0 ; Second half of FCB +ccp_fcb_cr DB 0x00 ; Current record +ccp_fcb_rn DB 0x00 ; Random access record number + DW 0x00 + DW 0x00 + DW 0x00 + +ccp_vars_end EQU $ + +; -------------------------------------------------- +; Disk parameters headers/blocks in RAM +; -------------------------------------------------- +DPH_RAM DS 16, 0 ; 0xbade +DPH1_RAM DS 16, 0 ; 0xbaee +DPH2_RAM DS 16, 0 ; 0xbafe +tmp_buff9 DS 9, 0 +DPB_A_RAM DS 15, 0 ; 0xbb17 +DPB_B_RAM DS 15, 0 ; 0xbb26 +DPB_C_RAM DS 15, 0 ; 0xbb35 + +; -------------------------------------------------- +cur_disk DB 0x00 ; 0xbb44 +dma_addr DW 0x0000 ; 0xbb46 +curr_track DB 0x00 ; 0xbb47 +curr_sec DB 0x00 ; 0xbb48 +slicer_disk DB 0x00 ; 0xbb49 +slicer_track DB 0x00 ; 0xbb4a +slicer_real_sector DB 0x00 ; 0xbb4b +tmp_slicer_real_sector DB 0x00 ; 0xbb4c +slicer_has_data DB 0x00 ; 0xbb4d +slicer_need_save DB 0x00 ; 0xbb4e +slicer_uninited_count DB 0x00 ; 0xbb4f +slicer_uninited_disk DB 0x00 ; 0xbb42 +slicer_uninited_track DB 0x00 ; 0xbb43 +slicer_uninited_sector_128 DB 0x00 ; 0xbb44 +tmp_slicer_result DB 0x00 ; 0xbb45 +tmp_slicer_can_read DB 0x00 ; 0xbb46 +tmp_slicer_operation DB 0x00 ; 0xbb47 +tmp_slicer_flush DB 0x00 ; 0xbb48 + +; Directory buffer used by BDOS for directory operations +dir_buffer DS DIR_BUFF_SIZE, 0 ; 0xbb49 + +; Allocation map for drive A (Mark used allocation blocks) +AL_MAP_A DS 31, 0 ; 248 bit +; Check vector for drive A (Checksumm of dir entries, used to detect disk change) +CHK_VEC_A DS 16, 0 ; 0xbbf6 + +; Allocation map for drive B +AL_MAP_B DS 45, 0 ; 184 bit +; Check vector for drive B +CHK_VEC_B DS 32, 0 ; 0xbc33 + +; Allocation map for drive C +AL_MAP_C DS 45, 0 ; 184 bit +; Check vector for drive C +CHK_VEC_C DS 32, 0 ; 0xbc80 + DS 96, 0 + +slicer_buffer DS 512, 0 ; 0xbd00 + +cpm_vars_end EQU $ + +ccp_vars_size EQU ccp_vars_end-cpm_vars_start +cpm_vars_size EQU cpm_vars_end-cpm_vars_start + + ; check integrity of original ROM + ASSERT tmp_dir_total = 0xba9a + ASSERT saved_stack_ptr = 0xba9c + ASSERT ccp_fcb_fn = 0xbab7 + ASSERT DPH_RAM = 0xbade + ASSERT cur_disk = 0xbb44 + ASSERT slicer_uninited_count = 0xbb4f + ASSERT slicer_buffer = 0xbd00 + + DISPLAY "| CCP_VARS\t| ",/H,cpm_vars_start," | ",/H,cpm_vars_size," |\t |" + + DISPLAY "cur_disk:",/H,cur_disk + + ENDMODULE + + ENDIF diff --git a/CPM_v2.2_r8_ddd05ed7/equates.inc b/CPM_v2.2_r8_ddd05ed7/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/io.inc b/CPM_v2.2_r8_ddd05ed7/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/mon_entries.inc b/CPM_v2.2_r8_ddd05ed7/mon_entries.inc new file mode 100644 index 0000000..b607096 --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON + DEFINE _MON + + MODULE MON + +mon_init EQU 0xe000 +mon_cold_start EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mon_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/CPM_v2.2_r8_ddd05ed7/ram.inc b/CPM_v2.2_r8_ddd05ed7/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/CPM_v2.2_r8_ddd05ed7/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/MON_Turbo/.gitignore b/MON_Turbo/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/MON_Turbo/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/MON_Turbo/.vscode/extensions.json b/MON_Turbo/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/MON_Turbo/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/MON_Turbo/.vscode/tasks.json b/MON_Turbo/.vscode/tasks.json new file mode 100644 index 0000000..d38fefc --- /dev/null +++ b/MON_Turbo/.vscode/tasks.json @@ -0,0 +1,65 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make MONITOR (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=turbo_mon.sld", + "--sym=turbo_mon.labels", + "--raw=turbo_mon.obj", + "--fullpath", + "turbo_mon.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Optimize MONITOR (MDL)", + "type": "shell", + "command": "/opt/java21/bin/java -jar ~/Soft/MDL/mdl.jar monitor.asm -cpu z80 -so -dialect sjasmplus", + "group": "build", + "problemMatcher": { + "applyTo": "allDocuments", + "fileLocation": [ + "autoDetect", + "${workspaceFolder}" + ], + "pattern": [ + { + "regexp": "^(\\w+): (.+) in (.+)#([0-9]+): (.+)$", + "file": 3, + "line": 4, + "severity": 1, + "message": 5, + "code": 2 + } + ] + }, + "presentation": { + "echo": false, + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": true, + "revealProblems": "onProblem" + } + } + + ] +} \ No newline at end of file diff --git a/MON_Turbo/BIN/MON_Turbo_bcac5ca0.BIN b/MON_Turbo/BIN/MON_Turbo_bcac5ca0.BIN new file mode 100644 index 0000000..9fc1a6f Binary files /dev/null and b/MON_Turbo/BIN/MON_Turbo_bcac5ca0.BIN differ diff --git a/MON_Turbo/README.md b/MON_Turbo/README.md new file mode 100644 index 0000000..a46a008 --- /dev/null +++ b/MON_Turbo/README.md @@ -0,0 +1,15 @@ +# Ocean-240.2 ROM Turbo monitor by Azmaster + +**CRC32 checksum**: bcac5ca0 + +Source codes of Turbo Monitor for Ocean-240.2 with Floppy controller. + +In Z80 mnemonics, but limited for i8080 instruction set. + +## Compile: + +Code is located in memory at address: 0xE000..0xFFFF + + sjasmplus --sld=turbo_mon.sld --sym=turbo_mon.labels --raw=turbo_mon.obj --fullpath turbo_mon.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/MON_Turbo/bios_entries.inc b/MON_Turbo/bios_entries.inc new file mode 100644 index 0000000..117a2c8 --- /dev/null +++ b/MON_Turbo/bios_entries.inc @@ -0,0 +1,41 @@ + +; ======================================================= +; Ocean-240.2 +; Definition of CPM BIOS entries to compile depended +; modules +; +; By Romych 2025-09-09 +; ====================================================== + IFNDEF _BIOS + DEFINE _BIOS + + MODULE BIOS + +boot_f EQU 0xD600 +wboot_f EQU 0xD603 +const_f EQU 0xD606 +conin_f EQU 0xD609 +conout_f EQU 0xD60C +list_f EQU 0xD60F +punch_f EQU 0xD612 +reader_f EQU 0xD615 +home_f EQU 0xD618 +seldsk_f EQU 0xD61B +settrk_f EQU 0xD61E +setsec_f EQU 0xD621 +setdma_f EQU 0xD624 +read_f EQU 0xD627 +write_f EQU 0xD62A +sectran_f EQU 0xD630 +reserved_f1 EQU 0xD633 +reserved_f2 EQU 0xD636 +tape_read_f EQU 0xD639 +tape_write_f EQU 0xD63C +tape_wait_f EQU 0xD63F + +bios_var04 EQU 0xD64B + + ENDMODULE + + + ENDIF diff --git a/MON_Turbo/equates.inc b/MON_Turbo/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/MON_Turbo/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/MON_Turbo/io.inc b/MON_Turbo/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/MON_Turbo/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/MON_Turbo/mon_entries.inc b/MON_Turbo/mon_entries.inc new file mode 100644 index 0000000..353393c --- /dev/null +++ b/MON_Turbo/mon_entries.inc @@ -0,0 +1,35 @@ +; ====================================================== +; Ocean-240.2 +; +; Monitor entries to compile CP/M modules +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _MON_ENTRY + DEFINE _MON_ENTRY + + MODULE MON_ENTRY + +mon_init EQU 0xe000 +mon_cold_start EQU 0xe003 +non_con_status EQU 0xe006 +mon_con_in EQU 0xe009 +mon_con_out EQU 0xe00c +mon_serial_in EQU 0xe00f +mpn_serial_out EQU 0xe012 +mon_char_print EQU 0xe015 +mon_tape_read EQU 0xe018 +mon_tape_write EQU 0xe01b +ram_disk_read EQU 0xe01e +ram_disk_write EQU 0xe021 +mon_res_f1 EQU 0xe024 +mon_res_f2 EQU 0xe027 +mon_tape_wait EQU 0xe02a +mon_tape_detect EQU 0xe02d +read_floppy EQU 0xe030 +write_floppy EQU 0xe033 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/MON_Turbo/ram.inc b/MON_Turbo/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/MON_Turbo/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/MON_Turbo/tm_vars.inc b/MON_Turbo/tm_vars.inc new file mode 100644 index 0000000..090d54d --- /dev/null +++ b/MON_Turbo/tm_vars.inc @@ -0,0 +1,90 @@ +; ======================================================= +; Ocean-240.2 +; Module TM_VARS - Turbo monitor variables +; RAM Range: 0xBA09-0xBFFF +; +; Disassembled by Romych 2025-09-09 +; ======================================================= + + IFNDEF _TM_VARS + DEFINE _TM_VARS + + MODULE TM_VARS + ORG 0xbf00 + +tm_stack ds 80 ; 0xbf00 search text or buffer to search? +tm_ltb ds 32 ; 0xbf50 +tm_strt ds 16 ; 0xbf70 +tm_track_no ds 1 ; 0xbf80 +tm_sector_no ds 1 ; 0xbf81 +tm_num_sect ds 1 ; 0xbf82 +tm_disk_op ds 1 ; 0xbf83 +tm_rw_disk ds 1 ; 0xbf84 +tm_drive ds 2 ; 0xbf85 +tm_tbu ds 1 ; 0xbf87 + +; user entered command buffer +tm_hrg ds 48 ; 0xbf88 +tm_stack_top ds 2 ; 0xbfb8 +tm_stsp ds 6 ; 0xbfba +rst_af_save ds 2 ; 0xbfc0 +rst_bc_save ds 2 ; 0xbfc2 +rst_de_save ds 2 ; 0xbfc4 +rst_hl_save ds 2 ; 0xbfc6 +rst_sp_save ds 2 ; 0xbfc8 +rst_ret_JP ds 1 ; 0xbfca +rst_ret_addr ds 2 ; 0xbfcb + ds 2 ; 0xbfcd + ds 3 ; 0xbfcf +tm_stack_0 ds 2 ; 0xbfd2 + ds 2 ; 0xbfd4 + ds 2 ; 0xbfd6 + ds 1 ; 0xbfd8 +m_esc_mode ds 1 ; 0xbfd9 +m_esc_cmd ds 1 ; 0xbfda +m_esc_param_cnt ds 1 ; 0xbfdb +m_esc_param_1 ds 1 ; 0xbfdc +m_esc_param_2 ds 1 ; 0xbfdd +m_esc_param_3 ds 1 ; 0xbfde +m_esc_param_4 ds 1 ; 0xbfdf +m_esc_hex_cmd ds 1 ; 0xbfe0 +m_esc_param_6 ds 1 ; 0xbfe1 + ds 4 ; 0xbfe2 +m_screen_mode ds 1 ; 0xbfe6 +m_cursor_row ds 1 ; 0xbfe7 +m_cursor_col ds 1 ; 0xbfe8 +m_curr_color ds 2 ; 0xbfe9 +m_row_shift ds 1 ; 0xbfeb +m_codepage ds 1 ; 0xbfec +m_cur_palette ds 1 ; 0xbfed +m_beep_period ds 2 ; 0xbfee +m_beep_duration ds 2 ; 0xbff0 +m_pix_shift ds 1 ; 0xbff2 +m_strobe_state ds 1 ; 0xbff3 +m_print_pos_x ds 2 ; 0xbff4 + ds 2 + ds 2 +m_last_key ds 1 ; 0xbffa +m_last_shifts ds 1 ; 0xbffb +mc_fix_state ds 1 ; 0xbffc +mc_stored_key ds 1 ; 0xbffd + + ASSERT tm_stack_top = 0xbfb8 + ASSERT tm_stack = 0xbf00 + ASSERT tm_hrg = 0xbf88 + ASSERT rst_hl_save = 0xbfc6 + ASSERT rst_ret_addr = 0xbfcb + ASSERT tm_stack_0 = 0xbfd2 + ASSERT m_esc_mode = 0xbfd9 + ASSERT m_screen_mode = 0xbfe6 + ASSERT mc_stored_key = 0xbffd + + ; DISPLAY " m_esc_mode is: ", m_esc_mode + ; DISPLAY " m_esc_mode is: ", m_esc_mode + ; DISPLAY " rst_ret_addr is: ", rst_ret_addr + ; DISPLAY " tm_stack_top is: ", tm_stack_top + ; DISPLAY " mc_stored_key is: ", mc_stored_key + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/MON_Turbo/turbo_mon.asm b/MON_Turbo/turbo_mon.asm new file mode 100644 index 0000000..dce4612 --- /dev/null +++ b/MON_Turbo/turbo_mon.asm @@ -0,0 +1,4787 @@ +; ====================================================== +; Ocean-240.2 +; Turbo monitor +; +; Disassembled by Romych 2025-09-09 +; ====================================================== + + DEVICE NOSLOT64K + + INCLUDE "io.inc" + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "bios_entries.inc" + + OUTPUT tmon_E000.bin + + + MODULE TURBO_MONITOR + + ORG 0xe000 + +; ------------------------------------------------------ +; Monitor Entry points +; ------------------------------------------------------ + +start: JP m_init ; E000 +mon_cold_start: JP m_cold_start ; E003 +non_con_status: JP m_con_status ; E009 +mon_con_in: JP tm_con_in ; E00C +mon_con_out: JP m_con_out ; E00F +mon_serial_in: JP m_serial_in ; E012 +mon_serial_out: JP m_serial_out ; E015 +mon_char_print: JP tm_char_print ; E018 +mon_tape_read: JP m_tape_read ; E01B +mon_tape_write: JP m_tape_write ; E01E +ram_disk_read: JP m_ramdisk_read ; E021 +ram_disk_write: JP m_ramdisk_write ; E024 +mon_free_fn1: JP m_cold_start ; E027 +mon_free_fn2: JP m_cold_start ; E02A +mon_tape_wait: JP m_tape_wait ; E02D +mon_tape_detect: JP m_tape_blk_detect ; E030 +read_floppy: JP m_read_floppy ; E033 +write_floppy: JP m_write_floppy ; E036 + + +; ------------------------------------------------------ +; Init system devices +; ------------------------------------------------------ +m_init: + DI + LD SP, TM_VARS.rst_ret_JP + LD A, 10000000b ; DD17 all ports to out + OUT (SYS_DD17CTR), A ; VV55 Sys CTR + OUT (DD67CTR), A ; VV55 Video CTR + + CALL m_init_kbd_tape + LD A, 01111111b ; VSU=0, C/M=1, FL=111, COL=111 + OUT (VID_DD67PB), A ; color mode + LD A, 00000001b + OUT (SYS_DD17PB), A ; Access to VRAM + LD B, 0x0 + LD HL, 0x3f00 + LD A, H + ADD A, 65 ; A=128 0x80 + + ; Clear memory from 0x3F00 to 0x7FFF +i_fill_video: + LD (HL), B + INC HL + CP H + JP NZ, i_fill_video + XOR A + OUT (SYS_DD17PB), A ; Disable VRAM + LD A, 00000111b + OUT (SYS_DD17PC), A ; pix shift to 7 + LD (TM_VARS.m_pix_shift), A + XOR A + LD (TM_VARS.m_screen_mode), A + LD (TM_VARS.m_row_shift), A + + ; Set color mode and palette + LD (TM_VARS.m_curr_color+1), A + CPL + LD (TM_VARS.m_curr_color), A + LD A, 00000011b + LD (TM_VARS.m_cur_palette), A + ; VSU=0, C/M=1, FL=000, COL=011 + ; color mode, black border + ; 00-black, 01-red, 10-purple, 11-white + LD A, 01000011b + OUT (VID_DD67PB), A + + ; config LPT + LD A, 0x4 + OUT (DD67PC), A ; bell=1, strobe=0 + LD (TM_VARS.m_strobe_state), A ; store strobe + LD HL,1024 ; 683us + LD (TM_VARS.m_beep_period), HL + LD HL, 320 ; 213us + LD (TM_VARS.m_beep_duration), HL + + ; Config UART + LD A, 11001110b + OUT (UART_DD72RR), A + LD A, 00100101b + OUT (UART_DD72RR), A + + ; Config Timer#1 for UART clock + LD A, 01110110b ; tmr#1, load l+m bin, sq wave + OUT (TMR_DD70CTR), A + + ; 1.5M/10 = 150kHz + LD A, 10 + OUT (TMR_DD70C2), A + XOR A + OUT (TMR_DD70C2), A + + ; Config PIC + LD A,00010010b ; ICW1 edge trigger, interval 8, sin... + OUT (PIC_DD75RS), A + XOR A + OUT (PIC_DD75RM), A ; ICW2 + CPL + OUT (PIC_DD75RM), A ; ICW3 no slave + LD A,00100000b + OUT (PIC_DD75RS), A ; Non-specific EOI command, End of I... + LD A, PIC_POLL_MODE + OUT (PIC_DD75RS), A ; Poll mode, poll on next RD + + ; Init cursor + CALL m_draw_cursor + LD HL, TM_VARS.tm_stsp + LD (TM_VARS.tm_stack_0), HL ; tm_stack_0 - directly not used elsewhere + LD A, JP_OPCODE + LD (RST1), A + LD HL, m_rst1_handler + LD (RST1_handler_addr), HL + + ; Beep + LD C, ASCII_BELL + CALL m_con_out + LD A, (BIOS.boot_f) + CP JP_OPCODE + JP Z, BIOS.boot_f + LD HL, msg_hw_mon + CALL m_out_strz + JP m_cold_start + +m_out_strz: + LD C, (HL) + LD A, C + OR A + RET Z + CALL m_con_out + INC HL + JP m_out_strz + +msg_hw_mon: + DB "\r\nHARDWARE MONITOR+ V1\r\n", 0 + DS 3, 0xFF + +m_init_kbd_tape: + LD A, 10010011b + ; Configure KBD/TAPE VV55 + ; A - In, B-Out, Ch-Out, Cl-In + OUT (KBD_DD78CTR), A + LD A, PORT_C4 + OUT (KBD_DD78PC), A + XOR A + OUT (KBD_DD78PC), A + XOR A + LD (TM_VARS.m_last_key), A + RET + +; ------------------------------------------------------ +; Console status +; Out: A = 0 - not ready +; A = 0xFF - ready (key pressed) +; ------------------------------------------------------ +m_con_status: + LD A, (TM_VARS.m_last_key) + OR A + JP Z, mc_check_irq + LD A, 0xff ; ready + RET + + ; ckeck keyboard IRQ +mc_check_irq: + IN A, (PIC_DD75RS) + AND KBD_IRQ + RET Z ; no int from keyboard + + ; read keyboard data + IN A, (KBD_DD78PA) + OR A + JP NZ, mc_has_key + LD A, KBD_ACK ; ACK=1 + OUT (KBD_DD78PC), A + XOR A + OUT (KBD_DD78PC), A ; ACK=0 + XOR A + RET + +mc_has_key: + PUSH BC + LD B, A ; store row in B + IN A, (KBD_DD78PC) + AND 00001111b ; column code + LD C, A + IN A, (KBD_DD78PB) ; [JST3..1, ACK,TAPE5,TAPE4,GK,GC] + RRA + AND 00110000b ; SHIFT+CTRL + OR C + LD C, A ; C=SHIFT+CTRL+IE10 + LD A,KBD_ACK ; ACK=1 + OUT (KBD_DD78PC), A + XOR A ; ACK=0 + OUT (KBD_DD78PC), A + LD A,B ; A = key + LD B, 0xff + + ; decode key +mc_calc_column: + INC B + RRA ; >> [CF] -> 7 + JP NC,mc_calc_column + LD A, C + RLA ; 0 <- [CF] << + RLA + RLA + + ; IE10 shifted + B + AND 01111000b + OR B + INC A + CP KEY_ALF + JP NZ, mc_not_alf + LD A, 0x0 + LD (TM_VARS.mc_stored_key), A + JP mc_fin + +mc_not_alf: + CP 0xc + JP NZ,mc_chk_fix + LD A, 0xff + LD (TM_VARS.mc_stored_key), A + JP mc_fin + +mc_chk_fix: + CP KEY_FIX + JP NZ,mc_plain_key + ; invert fix state + LD A, (TM_VARS.mc_fix_state) + CPL + LD (TM_VARS.mc_fix_state), A + JP mc_fin +mc_plain_key: + LD (TM_VARS.m_last_key), A + LD A, C + AND 0x30 + LD (TM_VARS.m_last_shifts), A + POP BC + LD A, 0xff + RET +mc_fin: + POP BC + XOR A + RET + +; ------------------------------------------------------ +; Read key +; Inp: A +; ------------------------------------------------------ +tm_con_in: + CALL m_con_status + OR A + JP Z, tm_con_in + LD A, (TM_VARS.m_last_key) + PUSH HL + PUSH BC + LD B, A + LD A, (TM_VARS.m_last_shifts) + LD C, A + LD HL, mci_ctrl_tab + AND 0x10 ; Ctrl + JP NZ, mci_is_crtl + LD A, C + LD HL, mci_alt_tab + AND 0x20 ; Shift + JP NZ, mci_is_shift + LD A, (TM_VARS.mc_fix_state) + OR A + JP NZ, mci_is_shift + LD HL, mci_base_tab + ; Calc offset for key decode +mci_is_shift: + LD A, B ; last + ADD A, L + LD L, A + LD A, H + ADC A, 0x0 + LD H, A + LD C, (HL) ; C - decoded + LD A, (TM_VARS.mc_stored_key) + OR A + LD A, C ; A = decoded key + JP Z, mci_key_zero + OR 0x80 +mci_key_zero: + LD C, A + XOR A + LD (TM_VARS.m_last_key), A + ; Return A=C=key + LD A, C + POP BC + POP HL + RET +mci_is_crtl: + LD A,B + ADD A,L + LD L, A + LD A,H + ADC A, 0x0 + LD H, A + LD A, (HL) + JP mci_key_zero + +mci_base_tab equ $-1 + db ',', '-', 0x00, 0x00, 0x00, '7', '8', '9' + db 0x1B, 0x09, 0x00, 0x00, 0x00, '0', '.', "\r" + db '@', 'J', 'F', 'Q', 0x00, '1', '2', '3' + db 0x9E, '1', 'C', 'Y', '^', '4', '5', '6' + db 0x81, '2', 'U', 'W', 'S', '+', 0x7F, 0x03 + db 0x86, '3', 'K', 'A', 'M', "\b", 0x99, 0x8B + db '4', 'E', 'P', 'I', ' ', 0x84, "\r", '/' + db 0x92, '5', 'N', 'R', 'T', 0x98, 0x85, '_' + db 0x83, '6', 'G', 'O', 'X', '.', ':', '-' + db '7', '[', 'L', 'B', 0x93, 0x5C, 'H', '0' + db '8', ']', 'D', ';', ',', 'V', 'Z', '9' + +mci_alt_tab equ $-1 + db ',', '-', 0x00, 0x00, 0x00, '7', '8', '9' + db 0x1B, "\t", 0x00, 0x00, 0x00, '0', '.', "\r" + db '`', 'j', 'f', 'q', 0x00, '1', '2', '3' + db 0x9E, '!', 'c', 'y', '~', '4', '5', '6' + db 0x81, '"', 'u', 'w', 's', '+', 0x7F, 0x03 + db 0x86, '#', 'k', 'a', 'm', "\b", 0x99, 0x8B + db '$', 'e', 'p', 'i', ' ', 0x84, "\r", '?' + db 0x92, '%', 'n', 'r', 't', 0x98, 0x85, '_' + db 0x83, '&', 'g', 'o', 'x', '>', '*', '=' + db 0x27, '{', 'l', 'b', 0x93, '|', 'h', '0' + db '(', '}', 'd', '+', '<', 'v', 'z', ')' + +mci_ctrl_tab equ $-1 + db ',', '-', 0x00, 0x00, 0x00, '7', '8', '9' + db 0x1B, "\t", 0x00, 0x00, 0x00, '0', '.', "\r" + db 0x00, "\n", 0x06, 0x11, 0x00, '1', '2', '3' + db 0x9E, '1', 0x03, 0x19, 0x1E, '4', '5', '6' + db 0x81, '2', 0x15, 0x17, 0x13, '+', 0x7F, 0x03 + db 0x86, '3', "\v", 0x01, "\r", "\b", 0x99, 0x8B + db '4', 0x05, 0x10, "\t", ' ', 0x84, "\r", '/' + db 0x92, '5', 0x0E, 0x12, 0x14, 0x98, 0x85, 0x1F + db 0x83, '6', "\a", 0x0F, 0x18, '.', ':', '-' + db '7', 0x1B, "\f", 0x02, 0x93, 0x1C, "\b", '0' + db '8', 0x1D, 0x04, ';', ',', 0x16, 0x1A, '9' + +; ------------------------------------------------------ +; Out char to console +; Inp: C - char +; ------------------------------------------------------ +m_con_out: + PUSH HL + PUSH DE + PUSH BC + CALL m_con_out_int + POP BC + POP DE + POP HL + RET + +; ------------------------------------------------------ +; Out char C to console +; ------------------------------------------------------ +m_con_out_int: + LD DE, TM_VARS.m_esc_mode + LD A, (DE) + DEC A + OR A + JP M, co_print_no_esc ; standart print no ESC mode + JP NZ, co_exit_esc + + ; handle ESC param + INC DE + LD A, (DE) + OR A + JP P, co_get_esc_param + LD A, C + AND 0xf ; convert char to command code + LD (DE), A + INC DE + XOR A + LD (DE), A + RET + +co_get_esc_param: + LD HL, TM_VARS.m_esc_cmd + LD B, (HL) + ; inc param count + INC HL + LD A, (HL) + INC A + LD (HL), A + ; store new param + LD E, A + LD D, 0x0 + ADD HL, DE + LD (HL), C + ; get params count for esc command + LD HL, m_esc_params_tab + LD E, B ; d=0, b = cmd + ADD HL, DE ; DE - command offset + CP (HL) + ; return if enough + RET M + + ; Entry point for user programs + ; to use graphics with parameters in ASCII form + LD A, (TM_VARS.m_esc_cmd) + AND 0xf ; ??? already applied + CP 15 + JP Z, ge_lbl_1 + CP 11 + LD C, 0x5 + JP Z, ge_lbl_2 + CP 4 + JP P, esc_handler1 +ge_lbl_1: + LD C, 0x4 +ge_lbl_2: + LD HL, TM_VARS.m_esc_param_1 + LD D, H + LD E, L +ge_lbl_3: + + LD A, (HL) + CP 0x3a ; ':' + JP M, ge_is_digit_1 + SUB 0x7 +ge_is_digit_1: + AND 0xf + ADD A, A + ADD A, A + ADD A, A + ADD A, A + LD B, A ; B=A*16 + INC HL + LD A, (HL) + CP ':' + JP M, ge_is_digit_2 + SUB 0x7 +ge_is_digit_2: + AND 0xf + OR B + INC HL + LD (DE), A + INC DE + DEC C + JP NZ, ge_lbl_3 + +esc_handler1: + LD HL, TM_VARS.m_esc_cmd + LD A, (HL) + AND 0xf + LD E, A + DEC HL + OR A + LD (HL), 0x2 + RET Z + LD D, 0x0 + LD (HL), D + DEC DE + ; Calc ESC command handler offset + LD HL, m_esc_handler_tab + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + ; HL = addr of handler func + LD D, (HL) + EX DE, HL + ; It is 1..4 func DRAW_* func? + CP 0x4 + JP P, esc_no_draw_fn + LD A, (TM_VARS.m_screen_mode) + AND 00000111b + ; If not in graphics mode - exit + JP NZ, esc_exit + +esc_no_draw_fn: + LD DE, esc_exit + PUSH DE + + ; Jump to ESC func handler + JP (HL) + +esc_exit: + XOR A + LD (TM_VARS.m_esc_mode), A + RET + + ; Count of parameters for ESC commands +m_esc_params_tab: + db 4, 8, 8, 4, 1, 2, 1, 1 + db 1, 1, 1, 10, 1, 1, 1, 8 + +m_esc_handler_tab: + dw esc_draw_fill_rect ; 1 + dw esc_draw_line ; 2 + dw esc_draw_dot ; 3 + dw esc_set_color ; 4 + dw esc_set_cursor ; 5 + dw esc_set_vmode ; 6 + dw esc_set_charset ; 7 + dw esc_set_palette ; 8 + dw esc_reset_esc ; 9 + dw esc_print_screen ; : + dw esc_fn_b ; ; + dw esc_fn_none ; < + dw esc_fn_none ; = + dw esc_fn_none ; > + dw esc_set_beep ; ? +esc_fn_none: + RET + +esc_set_beep: + LD DE, TM_VARS.m_esc_param_1 + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (TM_VARS.m_beep_period), HL + INC DE + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (TM_VARS.m_beep_duration), HL + RET + +esc_reset_esc: + POP DE + XOR A + LD (TM_VARS.m_esc_mode), A + LD A, (TM_VARS.m_screen_mode) + RET + +esc_print_screen: + LD A, (TM_VARS.m_screen_mode) + AND 00000111b + RET NZ ; ret if not 0 mode + LD DE, 0x30ff + CALL m_print_hor_line + DEC E + LD D, 0xf0 + +fna_chk_keys: + CALL m_con_status + OR A + JP Z, fna_no_keys + CALL tm_con_in + CP ASCII_ESC + RET Z + +fna_no_keys: + CALL m_print_hor_line + DEC E + JP NZ, fna_chk_keys + LD D, 0xe0 + CALL m_print_hor_line + RET + +; ------------------------------------------------------ +; Print line to printer +; D - width +; ------------------------------------------------------ +m_print_hor_line: + LD HL, cmd_esc_set_X0 + + ; Set printer X coordinate = 0 + CALL m_print_cmd + LD HL, 4 + LD (TM_VARS.m_print_pos_x), HL ; Set start coord X = 4 + LD B, 0x0 + +phl_print_next_col: + LD C, 0x0 + ; 1 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (TM_VARS.m_print_pos_x) + INC HL + + ; inc X + LD (TM_VARS.m_print_pos_x), HL + LD C, 0x1 + ; 2 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (TM_VARS.m_print_pos_x) + INC HL + ; inc X + LD (TM_VARS.m_print_pos_x), HL + INC B + LD A, B + CP 236 + JP C, phl_print_next_col + LD HL, cmd_esc_inc_Y2 + CALL m_print_cmd + RET + +; ------------------------------------------------------ +; Send command to printer +; Inp: HL -> to command bytes array +; ------------------------------------------------------ +m_print_cmd: + PUSH BC +ps_print_nxt: + LD A, (HL) + CP ESC_CMD_END + JP Z, ps_cmd_end + LD C, A + CALL m_print_write + INC HL + JP ps_print_nxt +ps_cmd_end: + POP BC + RET + +; ------------------------------------------------------ +; Print 7 vertical pixels to printer +; Inp: A - value to print +; ------------------------------------------------------ +m_print_vert_7pix: + PUSH AF + ; Set coordinate X to 0 + LD HL, cmd_esc_set_X + CALL m_print_cmd + LD HL, (TM_VARS.m_print_pos_x) + LD C,H + CALL m_print_write + LD C,L + CALL m_print_write + ; Set column print mode + LD HL, cmd_esc_print_col + CALL m_print_cmd + POP AF + ; Print 7 vertical pixels + LD C, A + CALL m_print_write + RET + +; ------------------------------------------------------ +; Control codes for printer УВВПЧ-30-004 +; ------------------------------------------------------ +; Zn - Increment Y coordinate +cmd_esc_inc_Y2: + db ASCII_ESC + db 'Z' + db 2h + db ESC_CMD_END + +; Xnn - Set X coordinate +cmd_esc_set_X0: + db ASCII_ESC + db 'X' + db 0h ; 0..479 + db 0h + db ESC_CMD_END +; ------------------------------------------------------ +; X - Start on "Set X coordinate" command +; ------------------------------------------------------ +cmd_esc_set_X: + db ASCII_ESC + db 'X' + db ESC_CMD_END + +; O - Column print (vertical 7 bit) +cmd_esc_print_col: + db ASCII_ESC + db 'O' + db ESC_CMD_END + +; ------------------------------------------------------ +; Get 7 vertical pixels from screen +; Inp: C - sheet +; Out: A - byte +; ------------------------------------------------------ +m_get_7vpix: + LD A, (TM_VARS.m_row_shift) + ADD A, B + ADD A, 19 ; skip first 20pix + LD L, A + PUSH DE + PUSH BC + LD A, E +g7v_calc_pix_no: + AND 0x7 + LD B, A + LD A, E + ; calc hi addr + RRA ; /8 + RRA + RRA + AND 0x1f + ADD A, A ; *2 + ADD A, 64 ; bytes per row + LD H, A + ; select sheet 0|1 + LD A, C + AND 0x1 + ADD A, H + LD H, A + ; HL = pix addr, turn on VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD E, (HL) ; read pixel + INC H ; HL += 512 + INC H + LD D, (HL) ; read pixel row+1 + + ; turn off VRAM access + XOR A + OUT (SYS_DD17PB), A +g7v_for_all_pix: + DEC B + JP M,g7v_all_shifted + ; shift pixels D >> [CF] >> E + LD A, D + RRA + LD D, A + LD A, E + RRA + LD E, A + JP g7v_for_all_pix +g7v_all_shifted: + LD A, E + LD D,00000000b + RRA + JP NC,g7v_not_1_1 + LD D,00110000b +g7v_not_1_1: + RRA + JP NC,g7v_not_1_2 + LD A, D + OR 11000000b + LD D, A +g7v_not_1_2: + LD A, D + POP BC + POP DE + RET + +esc_set_palette: + LD A, (TM_VARS.m_esc_param_1) + AND 00111111b ; bgcol[2,1,0],pal[2,1,0] + LD (TM_VARS.m_cur_palette), A + LD B, A + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + LD A, 0x0 + JP NZ,esp_no_colr + LD A, 0x40 + +esp_no_colr: + OR B + OUT (VID_DD67PB), A + RET + +esc_set_charset: + LD A, (TM_VARS.m_esc_param_1) + AND 0x3 ; charset 0..3 + LD (TM_VARS.m_codepage), A + RET + +; ------------------------------------------------------ +; Get address for draw symbol glyph +; ------------------------------------------------------ +m_get_glyph: + LD L, A + LD E, A + XOR A + LD D, A + LD H, A + ; HL = DE = A + ADD HL, HL + ADD HL, DE + ADD HL, HL + ADD HL, DE + ; HL = A * 7 + LD A, E ; A = A at proc entry + CP '@' + ; First 64 symbols is same for all codepages + JP M, m_cp_common + LD A, (TM_VARS.m_codepage) + OR A + ; cp=0 - Latin letters + JP Z, m_cp_common + DEC A + ; cp=1 - Russian letters + JP Z, m_cp_russ + ; cp=2 - 0x40..0x5F - displayed as Lat + ; 0x60 - 0x7F - displayed as Rus + LD A, E + CP 0x60 + JP M, m_cp_common +m_cp_russ: + LD DE, 448 ; +448=64*7 Offset for cp1 + ADD HL, DE + +m_cp_common: + LD DE, m_font_cp0-224 ; m_font_cp0-224 + ADD HL, DE ; add symbol glyph offset + RET + +; ------------------------------------------------------ +; Console output +; ------------------------------------------------------ +co_print_no_esc: + LD A, C + AND 0x7f + CP ' ' + JP M, m_print_control_char + CALL m_get_glyph + ; Calc screen address to DE + EX DE, HL + LD HL, (TM_VARS.m_cursor_row) + LD A, (TM_VARS.m_row_shift) + ADD A, L + LD L, A + LD A, H + ADD A, 64 + LD H, A + LD C, 7 + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + JP NZ, co_m_no_color + ; Access to video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + EX DE, HL + ; draw to both planes + XOR A + LD (DE), A + INC D + LD (DE), A + DEC D + INC E +co_m_colorify: + LD A, (TM_VARS.m_curr_color) + AND (HL) + ADD A, A + LD (DE), A + INC D + LD A, (TM_VARS.m_curr_color+1) + AND (HL) + ADD A, A + LD (DE), A + + ; next font byte + DEC D + INC HL + INC E + DEC C + JP NZ, co_m_colorify + XOR A + ; Remove access to VRAM + OUT (SYS_DD17PB), A + ; Address to draw cursor proc on stack + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row + LD A, (TM_VARS.m_screen_mode) + AND 0x8 + JP NZ, m_cursor_rt_2 + +m_psc_fwd_cmn: + INC HL + LD A, (HL) + ADD A, 0x2 + AND 0x3f + LD (HL), A + DEC HL + RET NZ +m_psc_lf_cmn: + LD A, (HL) + ADD A, 0xe + CP 0xfa + JP NC, LAB_ram_e57a + LD (HL), A + RET +LAB_ram_e57a: + LD A, (TM_VARS.m_row_shift) + ADD A, 0xe + OUT (SYS_DD17PA), A + LD (TM_VARS.m_row_shift), A + LD HL, 0x40f0 + ADD A, L + LD L, A + DEC L + DEC L + LD C, H + LD A, 0x1 + OUT (SYS_DD17PB), A + XOR A + LD DE, 0x1240 + +LAB_ram_e594: + LD H, C + LD B, E +LAB_ram_e596: + LD (HL), A + INC H + DEC B + JP NZ, LAB_ram_e596 + INC L + DEC D + JP NZ, LAB_ram_e594 + XOR A + OUT (SYS_DD17PB), A + RET + +m_psc_bksp_cmn: + INC HL + LD A, (HL) + SUB 0x2 + AND 0x3f + LD (HL), A + CP 0x3e + DEC HL + RET NZ + +m_psc_up_cmn: + LD A, (HL) + SUB 14 + JP NC, up_no_minus + LD A, 238 +up_no_minus: + LD (HL), A + RET + +m_psc_tab_cmn: + INC HL + LD A, (HL) + ADD A,16 + AND 0x30 + LD (HL), A + DEC HL + RET NZ + JP m_psc_lf_cmn + +m_psc_tab: + INC HL + LD A, (HL) + ADD A, 16 + AND 0x30 + LD (HL), A + DEC HL + RET NZ + JP m_psc_lf + + ; Move cursor 2 sym right, move to next line if wrap +m_cursor_rt_2: + INC HL + LD A, (HL) + ADD A,2 + AND 0x3f ; screen column 0..63 + LD (HL), A + DEC HL + RET NZ ; Return if no wrap + +m_psc_lf: + LD A, (HL) + ADD A, 12 + CP 16 + JP NC, mp_next_nowr + LD (HL), A + RET + +mp_next_nowr: + LD A, (TM_VARS.m_row_shift) + LD L, A + ADD A, 12 + LD E, A + LD C, 8 + ; Acces VRAM + LD A,1 + OUT (SYS_DD17PB), A +LAB_ram_e5f2: + LD B, 0x40 + LD H, 0x40 + LD D, H +LAB_ram_e5f7: + LD A, (DE) + LD (HL), A + INC H + INC D + DEC B + JP NZ, LAB_ram_e5f7 + INC L + INC E + DEC C + JP NZ, LAB_ram_e5f2 + LD C, 0xc + LD A, (TM_VARS.m_row_shift) + ADD A, 0x8 + LD E, A +LAB_ram_e60d: + LD B, 0x40 + LD D, 0x40 + XOR A +LAB_ram_e612: + LD (DE), A + INC D + DEC B + JP NZ, LAB_ram_e612 + INC E + DEC C + JP NZ, LAB_ram_e60d + XOR A + OUT (SYS_DD17PB), A + RET + +m_psc_bksp: + INC HL + LD A, (HL) + OR A + DEC HL + RET Z + INC HL + SUB 0x2 + AND 0x3f + LD (HL), A + DEC HL + RET + +co_m_no_color: + CP 7 + JP Z, LAB_ram_e6b5 + CP 3 + JP Z, LAB_ram_e6b5 + AND 0x2 + JP NZ, LAB_ram_e7b9 + LD A, 0x1 + OUT (SYS_DD17PB), A + EX DE, HL + XOR A + LD (DE), A + INC E + +LAB_ram_e645: + LD A, (HL) + ADD A, A + LD (DE), A + INC HL + INC E + DEC C + JP NZ, LAB_ram_e645 + XOR A + OUT (SYS_DD17PB), A + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row +LAB_ram_e658: + INC HL + LD A, (HL) + ADD A, 0x1 + AND 0x3f + LD (HL), A + DEC HL + RET NZ +LAB_ram_e661: + LD A, (HL) + ADD A, 0xb + CP 0xfa + JP NC, LAB_ram_e66b + LD (HL), A + RET + +LAB_ram_e66b: + LD A, (TM_VARS.m_row_shift) + ADD A, 0xb + OUT (SYS_DD17PA), A + LD (TM_VARS.m_row_shift), A + LD HL, 0x40f0 + ADD A,L + LD L, A + LD C,H + LD A, 0x1 + OUT (SYS_DD17PB), A + XOR A + LD DE, 0x1040 +LAB_ram_e683: + LD H, C + LD B, E +LAB_ram_e685: + LD (HL), A + INC H + DEC B + JP NZ, LAB_ram_e685 + INC L + DEC D + JP NZ, LAB_ram_e683 + XOR A + OUT (SYS_DD17PB), A + RET +LAB_ram_e694: + INC HL + LD A, (HL) + SUB 0x1 + AND 0x3f + LD (HL), A + CP 0x3f + DEC HL + RET NZ +LAB_ram_e69f: + LD A, (HL) + SUB 0xb + JP NC, LAB_ram_e6a7 + LD A, 0xf2 +LAB_ram_e6a7: + LD (HL), A + RET +LAB_ram_e6a9: + INC HL + LD A, (HL) + ADD A, 0x8 + AND 0x38 + LD (HL), A + DEC HL + RET NZ + JP LAB_ram_e661 +LAB_ram_e6b5: + CALL m_calc_addr_40 + LD A, 0x1 + OUT (SYS_DD17PB), A + EX DE, HL + LD A,B + OR B + JP Z, LAB_ram_e6cd + DEC B + JP Z, LAB_ram_e6df + DEC B + JP Z, LAB_ram_e706 + JP LAB_ram_e731 +LAB_ram_e6cd: + XOR A + LD (DE), A + INC E +LAB_ram_e6d0: + LD B, (HL) + LD A, (DE) + AND 0xc0 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, LAB_ram_e6d0 + JP LAB_ram_e745 +LAB_ram_e6df: + XOR A + LD (DE), A + DEC D + LD (DE), A + INC D + INC E +LAB_ram_e6e5: + LD A, (HL) + RRCA + RRCA + AND 0x7 + LD B, A + LD A, (DE) + AND 0xf0 + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + AND 0xc0 + LD B, A + DEC D + LD A, (DE) + AND 0x1f + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, LAB_ram_e6e5 + JP LAB_ram_e745 +LAB_ram_e706: + XOR A + LD (DE), A + DEC D + LD (DE), A + INC D + INC E +LAB_ram_e70c: + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0x1 + LD B, A + LD A, (DE) + AND 0xfc + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0xf0 + LD B, A + DEC D + LD A, (DE) + AND 0x7 + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, LAB_ram_e70c + JP LAB_ram_e745 +LAB_ram_e731: + DEC D + XOR A + LD (DE), A + INC E +LAB_ram_e735: + LD A, (HL) + RLCA + RLCA + LD B, A + LD A, (DE) + AND 0x1 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, LAB_ram_e735 + INC D +LAB_ram_e745: + XOR A + OUT (SYS_DD17PB), A + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row +LAB_ram_e74f: + INC HL + LD A, (HL) + ADD A, 0x1 + AND 0x7f + LD (HL), A + CP 0x50 + DEC HL + RET M +LAB_ram_e75a: + INC HL + XOR A + LD (HL), A + DEC HL +LAB_ram_e75e: + LD A, (HL) + ADD A, 0xb + CP 0xfa + JP NC, LAB_ram_e66b + LD (HL), A + RET +LAB_ram_e768: + INC HL + LD A, (HL) + SUB 0x1 + AND 0x7f + CP 0x7f + JP Z, LAB_ram_e776 + LD (HL), A + DEC HL + RET +LAB_ram_e776: + LD A, 0x4f + LD (HL), A + DEC HL +LAB_ram_e77a: + LD A, (HL) + SUB 0xb + JP NC, LAB_ram_e782 + LD A, 0xf2 +LAB_ram_e782: + LD (HL), A + RET +LAB_ram_e784: + INC HL + LD A, (HL) + ADD A, 0x8 + AND 0x7f + LD (HL), A + CP 0x50 + DEC HL + RET M + JP LAB_ram_e75a + +; ------------------------------------------------------ +; Calculate text position in 40 column text mode +; Out: HL - addr +; B - bit no +; C = 7 +; ------------------------------------------------------ +m_calc_addr_40: + LD HL, (TM_VARS.m_cursor_row) + LD A, (TM_VARS.m_row_shift) + ADD A,L + LD L, A ; HL = row+shift + LD A,H + CP 4 + LD B, A + JP M, ca_bef_scrn_top + AND 0x3 + LD B, A + LD A,H + OR A + RRA + OR A + RRA + LD C, A + LD H, 0x3 + XOR A + +ca_lbl1: + ADD A,H + DEC C + JP NZ, ca_lbl1 + ADD A,B +ca_bef_scrn_top: + ADD A, 0x40 ; next row + LD H, A + LD C, 0x7 + RET + +LAB_ram_e7b9: + LD A, (TM_VARS.m_cursor_col) + CP 0x40 + JP M, LAB_ram_e7c8 + LD HL, TM_VARS.m_cursor_row + CALL m_draw_cursor + RET +LAB_ram_e7c8: + LD A, 0x1 + OUT (SYS_DD17PB), A + EX DE, HL + XOR A + LD (DE), A + INC E +LAB_ram_e7d0: + LD A, (HL) + ADD A, A + LD (DE), A + INC HL + INC E + DEC C + JP NZ, LAB_ram_e7d0 + XOR A + OUT (SYS_DD17PB), A + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row + INC HL + LD A, (HL) + ADD A, 0x1 + CP 0x40 + JP M, LAB_ram_e7ee + LD A, 0x40 +LAB_ram_e7ee: + LD (HL), A + DEC HL + RET + +m_psc_clrscr_cmn: + LD A, (TM_VARS.m_screen_mode) + AND 0x8 + JP NZ,m_clr_color + LD A,01111111b + OUT (VID_DD67PB), A ; C/M=1 FL=111 CL=111 All black + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD DE, video_ram + EX DE, HL + LD A,H + ADD A,64 ; row + 1 + LD B, 0x0 + +clr_fill_scrn1: + LD (HL),B + INC HL + CP H + JP NZ, clr_fill_scrn1 + EX DE, HL + LD A, (TM_VARS.m_cur_palette) + LD B, A + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + LD A, 0x0 + JP NZ, clr_rest_no_color + LD A, 01000000b + +clr_rest_no_color: + OR B + ; Restore mode and palette + OUT (VID_DD67PB), A + +m_psc_home_cmn: + XOR A + NOP + NOP + LD (HL), A + INC HL + XOR A + LD (HL), A + DEC HL + XOR A + ; Disable VRAM access + OUT (SYS_DD17PB), A + RET + + ; Clear scr in color mode +m_clr_color: + LD A, (TM_VARS.m_row_shift) + LD L, A + LD C,20 + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A +m_clr_fill_c1: + LD H, 0x40 ; HL = 0x4000 + shift_row + LD B,64 ; 64 bytes at row + XOR A +m_clr_fill_c2: + LD (HL), A + INC H + DEC B + JP NZ, m_clr_fill_c2 + INC L + DEC C + JP NZ, m_clr_fill_c1 + XOR A + ; Disabe VRAM access + OUT (SYS_DD17PB), A + JP m_psc_home_cmn +m_draw_cursor: + LD A, (TM_VARS.m_screen_mode) + AND 0x4 + RET NZ + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + JP NZ, LAB_ram_e884 + EX DE, HL + LD HL, (TM_VARS.m_cursor_row) + LD A, (TM_VARS.m_row_shift) + ADD A, L + LD L, A + LD A, H + ADD A, 0x40 + LD H, A + LD A, 0x1 + OUT (SYS_DD17PB), A + LD BC, 0x7f08 +LAB_ram_e872: + LD A, (HL) + XOR B + LD (HL), A + INC H + LD A, (HL) + XOR B + LD (HL), A + DEC H + INC L + DEC C + JP NZ, LAB_ram_e872 + EX DE, HL + XOR A + OUT (SYS_DD17PB), A + RET +LAB_ram_e884: + CP 0x3 + JP Z, LAB_ram_e8af + EX DE, HL + LD HL, (TM_VARS.m_cursor_row) + LD A, (TM_VARS.m_row_shift) + ADD A, L + LD L, A + LD A, H + CP 0x40 + EX DE, HL + RET P + EX DE, HL + ADD A, 0x40 + LD H, A + LD A, 0x1 + OUT (SYS_DD17PB), A + LD BC, 0x7f08 +LAB_ram_e8a2: + LD A, (HL) + XOR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e8a2 + EX DE, HL + XOR A + OUT (SYS_DD17PB), A + RET +LAB_ram_e8af: + EX DE, HL + LD HL, (TM_VARS.m_cursor_row) + LD A, H + CP 0x50 + EX DE, HL + RET P + EX DE, HL + CALL m_calc_addr_40 + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, B + OR B + JP Z, LAB_ram_e8d0 + DEC B + JP Z, LAB_ram_e8e6 + DEC B + JP Z, LAB_ram_e908 + JP LAB_ram_e92a +LAB_ram_e8d0: + LD BC, 0x1f08 +LAB_ram_e8d3: + LD A, (HL) + AND 0xc0 + LD B, A + LD A, (HL) + XOR 0x1f + AND 0x1f + OR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e8d3 + JP LAB_ram_e93e +LAB_ram_e8e6: + LD C, 0x8 +LAB_ram_e8e8: + DEC H + LD A, (HL) + AND 0x1f + LD B, A + LD A, (HL) + XOR 0xc0 + AND 0xc0 + OR B + LD (HL), A + INC H + LD A, (HL) + AND 0xf0 + LD B, A + LD A, (HL) + XOR 0x7 + AND 0x7 + OR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e8e8 + JP LAB_ram_e93e +LAB_ram_e908: + LD C, 0x8 +LAB_ram_e90a: + DEC H + LD A, (HL) + AND 0x7 + LD B, A + LD A, (HL) + XOR 0xf0 + AND 0xf0 + OR B + LD (HL), A + INC H + LD A, (HL) + AND 0xfc + LD B, A + LD A, (HL) + XOR 0x1 + AND 0x1 + OR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e90a + JP LAB_ram_e93e +LAB_ram_e92a: + LD C, 0x8 + DEC H +LAB_ram_e92d: + LD A, (HL) + AND 0x1 + LD B, A + LD A, (HL) + XOR 0x7c + AND 0x7c + OR B + LD (HL), A + INC L + DEC C + JP NZ, LAB_ram_e92d + INC H +LAB_ram_e93e: + EX DE, HL + XOR A + OUT (SYS_DD17PB), A ; reset screen shifts + RET + +m_print_control_char: + CP ASCII_ESC + JP NZ,m_psc_std_char + ; turn on ESC mode for next chars + LD HL, TM_VARS.m_esc_mode + LD (HL), 0x1 + INC HL + LD (HL), 0xff + RET + +m_psc_std_char: + CP ASCII_BELL + JP Z, m_beep + LD HL, m_draw_cursor + PUSH HL + LD HL, TM_VARS.m_cursor_row + PUSH AF + CALL m_draw_cursor + LD A, (TM_VARS.m_screen_mode) + AND 0x8 ; mode 40x20? + JP Z, m_psc_no_40x30 ; jump if no + POP AF + CP ASCII_TAB ; TAB + JP Z,m_psc_tab + CP ASCII_BS ; BKSP + JP Z,m_psc_bksp + CP ASCII_CAN ; Cancel + JP Z,m_cursor_rt_2 + CP ASCII_US ; ASCII Unit separator + JP Z,m_clr_color + CP ASCII_LF ; LF + JP Z,m_psc_lf + CP ASCII_CR ; CR + RET NZ ; ret on unknown + INC HL + LD (HL), 0x0 + DEC HL + RET + + ; common for 40x25, 64x25, 80x25 modes +m_psc_no_40x30: + POP AF + CP ASCII_US ; Unit separator + JP Z, m_psc_clrscr_cmn + CP ASCII_FF ; Form feed + JP Z, m_psc_home_cmn + PUSH AF + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + JP NZ, LAB_ram_e9c6 + POP AF + CP ASCII_TAB + JP Z, m_psc_tab_cmn + CP ASCII_BS + JP Z, m_psc_bksp_cmn + CP ASCII_CAN + JP Z, m_psc_fwd_cmn + CP ASCII_EM ; ASCII End of medium + JP Z, m_psc_up_cmn + CP ASCII_SUB + JP Z, m_psc_lf_cmn ; cursor down + CP ASCII_LF + JP Z, m_psc_lf_cmn + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +LAB_ram_e9c6: + LD A, (TM_VARS.m_screen_mode) + CP 0x3 + JP Z, m_psc_no_40x25 + CP 0x7 + JP Z, m_psc_no_40x25 + AND 0x2 + JP NZ, LAB_ram_e9ff +; For 40x25? + POP AF + CP 0x9 + JP Z, LAB_ram_e6a9 + CP 0x8 + JP Z, LAB_ram_e694 + CP 0x18 + JP Z, LAB_ram_e658 + CP 0x19 + JP Z, LAB_ram_e69f + CP 0x1a + JP Z, LAB_ram_e661 + CP 0xa + JP Z, LAB_ram_e661 + CP 0xd + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +LAB_ram_e9ff: + POP AF + CP 0xa + JP Z, LAB_ram_e661 + CP 0xd + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +m_psc_no_40x25: + POP AF + CP 0x9 + JP Z, LAB_ram_e784 + CP 0x8 + JP Z, LAB_ram_e768 + CP 0x18 + JP Z, LAB_ram_e74f + CP 0x19 + JP Z, LAB_ram_e77a + CP 0x1a + JP Z, LAB_ram_e75e + CP 0xa + JP Z, LAB_ram_e75e + CP 0xd + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +m_beep: + LD HL, (TM_VARS.m_beep_duration) + EX DE, HL + LD HL, (TM_VARS.m_beep_period) + LD A,00110110b ; TMR#0 LSB+MSB Square Wave Generator + OUT (TMR_DD70CTR), A + LD A,L ; LSB + OUT (TMR_DD70C1), A + LD A,H ; MSB + OUT (TMR_DD70C1), A + LD A, (TM_VARS.m_strobe_state) + LD B, A +m_bell_cont: + LD A, D ; DE=duration + OR E + RET Z ; ret if enough + DEC DE + LD A,B + XOR BELL_PIN + LD B, A + OUT (DD67PC), A ; Invert bell pin +m_bell_wait_tmr1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; 0x10 + JP NZ,m_bell_wait_tmr1 + LD A,B + XOR BELL_PIN ; Flip pin again + LD B, A + OUT (DD67PC), A +m_bell_wait_tmr2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP Z,m_bell_wait_tmr2 + JP m_bell_cont + +; ------------------------------------------------------ +; 5 Set cursor position +; ------------------------------------------------------ +esc_set_cursor: + LD A, (TM_VARS.m_screen_mode) + AND 0x8 + RET NZ ; ret if graphics mode + CALL m_draw_cursor ; hide cursor + LD A, (TM_VARS.m_screen_mode) + AND 0x7 ; mode 0-7 + JP NZ,sc_set_64_or_80 + ; Set cursor for 32x18 mode + LD DE, TM_VARS.m_esc_param_1 + LD HL, TM_VARS.m_cursor_col + INC DE + LD A, (DE) ; column + AND 0x1f ; limit column to 0..31 + ADD A, A ; *2 + LD (HL), A + DEC DE + DEC HL + LD A, (DE) ; row + AND 0x1f ; 0..31 + CP 17 + JP C,sc_no_row_limit1 + LD A,17 + +sc_no_row_limit1: + LD B, A + ADD A, A + ADD A,B + ADD A, A + ADD A,B + ADD A, A ; a = a * 14 (font height) + LD (HL), A + CALL m_draw_cursor + RET + +sc_set_64_or_80: + LD A, (TM_VARS.m_screen_mode) + CP 0x3 + JP Z, sc_set_for_80x32 + CP 0x7 + JP Z, sc_set_for_80x32 + AND 0x2 + JP NZ, sc_set_for_65x23 + ; Set for 64 col modes + LD DE, TM_VARS.m_esc_param_1 ; row + LD HL, TM_VARS.m_cursor_col + INC DE + LD A, (DE) ; column + SUB 0x20 + AND 0x3f ; 0..63 + LD (HL), A + DEC DE + DEC HL + LD A, (DE) ; row + ; limit row to 0..22 + AND 0x1f + CP 22 + JP C,sc_no_row_limit2 + LD A,22 +sc_no_row_limit2: + LD B, A + ADD A, A + ADD A, A + ADD A,B + ADD A, A + ADD A,B ; A = A * 11 (font height) + LD (HL), A + CALL m_draw_cursor ; show cursor + RET + +sc_set_for_65x23: + LD DE, TM_VARS.m_esc_param_1 + LD HL, TM_VARS.m_cursor_col + INC DE + LD A, (DE) ; column + SUB 0x20 + CP 64 + JP M,sc_no_col_limit3 + LD A,64 + +sc_no_col_limit3: + LD (HL), A + DEC DE + DEC HL + LD A, (DE) ; row + AND 0x1f + CP 22 + JP C,sc_no_row_limit3 + LD A,22 +sc_no_row_limit3: + LD B, A + ADD A, A + ADD A, A + ADD A,B + ADD A, A + ADD A,B ; A = A * 11 + LD (HL), A + CALL m_draw_cursor ; show cursor + RET + +sc_set_for_80x32: + LD DE, TM_VARS.m_esc_param_1 + LD HL, TM_VARS.m_cursor_col + INC DE + LD A, (DE) + SUB 0x20 + AND 0x7f + CP 79 + JP M, sc_no_row_limit4 + LD A,79 + +sc_no_row_limit4: + LD (HL), A + DEC DE + DEC HL + LD A, (DE) + AND 0x1f + CP 22 + JP C,sc_no_col_limit4 + LD A,22 + +sc_no_col_limit4: + LD B, A + ADD A, A + ADD A, A + ADD A,B + ADD A, A + ADD A,B ; A = A * 11 + LD (HL), A + CALL m_draw_cursor ; show cursor + RET + +; ------------------------------------------------------ +; 6n +; where n is +; 0 - 32x18 with cursor; +; 1,2 - 64x23 with cursor; +; 3 - 80x23 with cursor; +; 4 - 32x18 no cursor; +; 5,6 - 64x23 no cursor; +; 7 - 80x23 no cursor; +; 8 - graphics mode. +; ------------------------------------------------------ +esc_set_vmode: + LD HL, TM_VARS.m_screen_mode + LD A, (TM_VARS.m_cur_palette) + LD B, A ; b = palette + LD A, (TM_VARS.m_esc_param_1) + LD C, A ; c = mode + AND 0x8 ; graph mode? + LD A, C + JP Z,svm_set_text + LD A, 0x8 +svm_set_text: + AND 0xf + LD (HL), A ; save new mode + AND 0x7 ; with cursor? + LD A,00000000b + JP NZ, swm_no_color + LD A,01000000b +swm_no_color: + OR B + OUT (VID_DD67PB), A ; Set C/M and palette + LD HL, TM_VARS.m_cursor_row + CALL m_psc_clrscr_cmn + CALL m_draw_cursor + RET + +; ------------------------------------------------------ +; 4n n=1..4 Set drawing color +; ------------------------------------------------------ +esc_set_color: + LD A, (TM_VARS.m_esc_param_1) + AND 0x3 + RRA + LD B, A + LD A, 0x0 + SBC A, A + LD (TM_VARS.m_curr_color), A + LD A, B + DEC A + CPL + LD (TM_VARS.m_curr_color+1), A + RET + +co_exit_esc: + LD A, (TM_VARS.m_screen_mode) + AND 0x7 + JP NZ, esc_exit + LD A, C + AND 0x7f + LD C, A + CP 0x20 + JP M, esc_exit + LD HL, TM_VARS.m_esc_param_1 + LD A, (HL) + LD E, A + ADD A, 0x8 + JP C, esc_exit + LD (HL), A + INC HL + LD A, 0xf7 + CP (HL) + JP C, esc_exit + LD D, (HL) + CALL SUB_ram_ebe2 + LD A, L + SUB 0x7 + LD L, A + PUSH HL + LD A, C + CALL m_get_glyph + POP DE + LD C, 0x7 + +LAB_ram_eb9b: + PUSH HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD L, (HL) + LD H, 0x0 + LD A, B + OR A + JP Z, LAB_ram_ebad +LAB_ram_eba8: + ADD HL, HL + DEC A + JP NZ, LAB_ram_eba8 +LAB_ram_ebad: + EX DE, HL + PUSH BC + LD A, (TM_VARS.m_curr_color) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + LD A, (TM_VARS.m_curr_color+1) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + POP BC + XOR A + OUT (SYS_DD17PB), A + POP HL + INC HL + DEC C + JP NZ, LAB_ram_eb9b + RET +SUB_ram_ebe2: + LD A, (TM_VARS.m_row_shift) + SUB D + DEC A + LD L, A + LD A, E + AND 0x7 + LD B, A + LD A, E + RRA + RRA + AND 0x3e + ADD A, 0x40 + LD H, A + RET + +esc_draw_fill_rect: + LD HL, TM_VARS.m_esc_param_4 + LD DE, TM_VARS.m_esc_param_2 + LD A, (DE) + LD B, (HL) + CP B + JP NC, LAB_ram_ec04 + LD (HL), A + LD A,B + LD (DE), A +LAB_ram_ec04: + DEC DE + DEC HL + LD A, (DE) + LD B, (HL) + CP B + JP C, LAB_ram_ec0f + LD (HL), A + LD A,B + LD (DE), A +LAB_ram_ec0f: + EX DE, HL + LD E, (HL) + INC HL + LD D, (HL) + CALL SUB_ram_ebe2 + PUSH HL + XOR A +LAB_ram_ec18: + SCF + RLA + DEC B + JP P, LAB_ram_ec18 + RRA + LD D, A + LD HL, TM_VARS.m_esc_param_3 + LD A, (HL) + AND 0x7 + LD B, A + XOR A +LAB_ram_ec28: + SCF + RLA + DEC B + JP P, LAB_ram_ec28 + CPL + LD E, A + LD A, (HL) + DEC HL + DEC HL + SUB (HL) + RRCA + RRCA + RRCA + AND 0x1f + LD C, A + INC HL + LD A, (HL) + INC HL + INC HL + SUB (HL) + JP NZ, LAB_ram_ec43 + INC A +LAB_ram_ec43: + LD B, A + POP HL + LD A, E + LD (TM_VARS.m_esc_hex_cmd), A +LAB_ram_ec49: + PUSH DE + PUSH HL + PUSH BC + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, C + OR A + JP NZ, LAB_ram_ec58 + LD A, D + OR E +dr_no_cmd: + LD D, A +LAB_ram_ec58: + LD B, D + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + INC H + LD A, C + OR A + JP Z, LAB_ram_ec81 + DEC C +LAB_ram_ec70: + LD A, (TM_VARS.m_esc_hex_cmd) + JP Z, dr_no_cmd +LAB_ram_ec76: + LD (HL), E + INC H + LD (HL), D + INC H + DEC C + JP NZ, LAB_ram_ec76 + JP LAB_ram_ec70 +LAB_ram_ec81: + XOR A + OUT (SYS_DD17PB), A + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, LAB_ram_ec49 + RET + +esc_draw_line: + LD HL, TM_VARS.m_esc_param_1 + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD A, (HL) + INC HL + LD H, (HL) + LD L, A + CP E + JP C, LAB_ram_ec9d + EX DE, HL +LAB_ram_ec9d: + LD (TM_VARS.m_esc_param_1), HL + LD A, E + SUB L + LD L, A + LD A, D + SUB H + LD H, A + PUSH AF + JP NC, LAB_ram_ecad + CPL + INC A + LD H, A +LAB_ram_ecad: + EX DE, HL + LD HL, (TM_VARS.m_esc_param_1) + EX DE, HL + JP Z, LAB_ram_ed96 + LD A, L + OR A + JP Z, LAB_ram_ed4c + LD B, A + POP AF + LD A, 0x0 + ADC A, A + LD (TM_VARS.m_esc_hex_cmd), A + LD E, H + LD C, 0x10 + LD D, 0x0 +LAB_ram_ecc7: + ADD HL, HL + EX DE, HL + ADD HL, HL + EX DE, HL + LD A, D + JP C, LAB_ram_ecd3 + CP B + JP C, LAB_ram_ecd6 +LAB_ram_ecd3: + SUB B + LD D, A + INC HL +LAB_ram_ecd6: + DEC C + JP NZ, LAB_ram_ecc7 + LD DE, 0x0 + PUSH DE + PUSH HL + LD HL, (TM_VARS.m_esc_param_1) + EX DE, HL + LD C,B + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_ece9: + RLCA + DEC B + JP P, LAB_ram_ece9 + CPL + LD B, A +LAB_ram_ecf0: + POP DE + EX (SP), HL + LD A,H + ADD HL, DE + SUB H + CPL + INC A + EX (SP), HL + PUSH DE + PUSH BC + LD C, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (TM_VARS.m_esc_hex_cmd) + OR A + JP NZ, LAB_ram_ed21 +LAB_ram_ed0b: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, LAB_ram_ed37 + DEC C + DEC L + JP LAB_ram_ed0b +LAB_ram_ed21: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, LAB_ram_ed37 + DEC C + INC L + JP LAB_ram_ed21 +LAB_ram_ed37: + XOR A + OUT (SYS_DD17PB), A + POP BC + LD A,B + SCF + RLA + JP C, LAB_ram_ed44 + RLA + INC H + INC H +LAB_ram_ed44: + LD B, A + DEC C + JP NZ, LAB_ram_ecf0 + POP HL + POP HL + RET +LAB_ram_ed4c: + LD C,H + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_ed52: + RLCA + DEC B + JP P, LAB_ram_ed52 + CPL + LD B, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + POP AF + LD A, 0x1 + OUT (SYS_DD17PB), A + JP C, LAB_ram_ed7c +LAB_ram_ed66: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, LAB_ram_ed92 + DEC C + DEC L + JP LAB_ram_ed66 +LAB_ram_ed7c: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, LAB_ram_ed92 + DEC C + INC L + JP LAB_ram_ed7c +LAB_ram_ed92: + XOR A + OUT (SYS_DD17PB), A + RET +LAB_ram_ed96: + POP AF + LD C,L + LD A,L + OR A + JP NZ, LAB_ram_ed9e + INC C +LAB_ram_ed9e: + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_eda3: + RLCA + DEC B + JP P, LAB_ram_eda3 + CPL + LD B, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A +LAB_ram_edb3: + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A,B + SCF + RLA + JP C, LAB_ram_edc8 + RLA + INC H + INC H +LAB_ram_edc8: + LD B, A + DEC C + JP NZ, LAB_ram_edb3 + XOR A + OUT (SYS_DD17PB), A + RET +esc_draw_dot: + LD HL, (TM_VARS.m_esc_param_1) + EX DE, HL + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_edda: + RLCA + DEC B + JP P, LAB_ram_edda + LD B, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + XOR B + LD (HL), A + INC H + LD A, (HL) + XOR B + LD (HL), A + XOR A + OUT (SYS_DD17PB), A + RET +esc_fn_b: + LD A, (TM_VARS.m_esc_param_3) + LD B, A + OR A + RET Z + LD A, 0x7f + CP B + RET M + XOR A + LD D, A + LD E,B + CALL SUB_ram_ee53 + LD A, 0x1 + LD H, A + SUB B + LD C, A + LD A,B + RLCA + LD B, A + LD A, 0x1 + SUB B + LD L, A + CCF +LAB_ram_ee11: + INC D + LD A, E + CP D + JP Z,SUB_ram_ee53 + CALL SUB_ram_ee53 + LD A,H + ADD A, 0x2 + LD H, A + LD A,L + ADD A, 0x2 + LD L, A + LD A, C + ADD A,H + LD C, A + JP NC, LAB_ram_ee11 +LAB_ram_ee28: + CCF + INC D + DEC E + LD A, D + CP E + JP Z,SUB_ram_ee53 + SUB E + CP 0x1 + RET Z + LD A, E + SUB D + CP 0x1 + JP Z,SUB_ram_ee53 + CALL SUB_ram_ee53 + LD A,H + ADD A, 0x2 + LD H, A + LD A,L + ADD A, 0x4 + LD L, A + JP NC, LAB_ram_ee4a + CCF +LAB_ram_ee4a: + LD A, C + ADD A,L + LD C, A + JP NC, LAB_ram_ee11 + JP LAB_ram_ee28 +SUB_ram_ee53: + PUSH HL + PUSH DE + PUSH BC + PUSH DE + CALL SUB_ram_ee6f + LD HL, (TM_VARS.m_esc_param_1) + CALL SUB_ram_eedc + POP DE + CALL SUB_ram_ee8f + LD HL, (TM_VARS.m_esc_param_1) + CALL SUB_ram_ef0b + POP BC + POP DE + POP HL + XOR A + RET +SUB_ram_ee6f: + LD HL, (TM_VARS.m_esc_param_4) + LD A,L + OR A + LD C, D + LD B, E + JP NZ, LAB_ram_ee7f + LD A,H + OR A + JP NZ, LAB_ram_ee88 + RET +LAB_ram_ee7f: + LD A,H + LD H,L + LD E, C +OFF_ram_ee82: + CALL SUB_ram_eeaf + LD C, E + OR A + RET Z +LAB_ram_ee88: + LD H, A + LD E,B + CALL SUB_ram_eeaf + LD B, E + RET +SUB_ram_ee8f: + LD HL, (TM_VARS.m_esc_param_4) + LD A,L + OR A + LD C, D + LD B, E + JP NZ, LAB_ram_ee9f + LD A,H + OR A + JP NZ, LAB_ram_eea8 + RET +LAB_ram_ee9f: + LD A,H + LD H,L + LD E,B + CALL SUB_ram_eeaf + LD B, E + OR A + RET Z +LAB_ram_eea8: + LD H, A + LD E, C + CALL SUB_ram_eeaf + LD C, E + RET +SUB_ram_eeaf: + LD D, 0x0 + LD L, D + ADD HL, HL + JP NC, LAB_ram_eeb7 + ADD HL, DE +LAB_ram_eeb7: + ADD HL, HL + JP NC, LAB_ram_eebc + ADD HL, DE +LAB_ram_eebc: + ADD HL, HL + JP NC, LAB_ram_eec1 + ADD HL, DE +LAB_ram_eec1: + ADD HL, HL + JP NC, LAB_ram_eec6 + ADD HL, DE +LAB_ram_eec6: + ADD HL, HL + JP NC, LAB_ram_eecb + ADD HL, DE +LAB_ram_eecb: + ADD HL, HL + JP NC, LAB_ram_eed0 + ADD HL, DE +LAB_ram_eed0: + ADD HL, HL + JP NC, LAB_ram_eed5 + ADD HL, DE +LAB_ram_eed5: + ADD HL, HL + JP NC, LAB_ram_eeda + ADD HL, DE +LAB_ram_eeda: + LD E,H + RET +SUB_ram_eedc: + LD A,H + ADD A,B + JP C, LAB_ram_eee8 + LD D, A + LD A,L + ADD A, C + LD E, A + CALL SUB_ram_ef3a +LAB_ram_eee8: + LD A,H + ADD A,B + JP C, LAB_ram_eef4 + LD D, A + LD A,L + SUB C + LD E, A + CALL SUB_ram_ef3a +LAB_ram_eef4: + LD A,H + SUB B + JP C, LAB_ram_ef00 + LD D, A + LD A,L + SUB C + LD E, A + CALL SUB_ram_ef3a +LAB_ram_ef00: + LD A,H + SUB B + RET C + LD D, A + LD A,L + ADD A, C + LD E, A + CALL SUB_ram_ef3a + RET +SUB_ram_ef0b: + LD A,H + ADD A, C + JP C, LAB_ram_ef17 + LD D, A + LD A,L + ADD A,B + LD E, A + CALL SUB_ram_ef3a +LAB_ram_ef17: + LD A,H + ADD A, C + JP C, LAB_ram_ef23 + LD D, A + LD A,L + SUB B + LD E, A + CALL SUB_ram_ef3a +LAB_ram_ef23: + LD A,H + SUB C + JP C, LAB_ram_ef2f + LD D, A + LD A,L + SUB B + LD E, A + CALL SUB_ram_ef3a +LAB_ram_ef2f: + LD A,H + SUB C + RET C + LD D, A + LD A,L + ADD A,B + LD E, A + CALL SUB_ram_ef3a + RET +SUB_ram_ef3a: + RET C + PUSH HL + PUSH BC + CALL SUB_ram_ebe2 + LD A, 0x80 +LAB_ram_ef42: + RLCA + DEC B + JP P, LAB_ram_ef42 + CPL + LD B, A + EX DE, HL + LD HL, (TM_VARS.m_curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + XOR A + OUT (SYS_DD17PB), A + POP BC + POP HL + RET + + ; Full charset, Common + Latin letters +m_font_cp0: + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 + db 0x04, 0x04, 0x04, 0x00, 0x00, 0x04, 0x14, 0x14 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x1F + db 0x0A, 0x1F, 0x0A, 0x0A, 0x04, 0x1E, 0x05, 0x0E + db 0x14, 0x0F, 0x04, 0x03, 0x13, 0x08, 0x04, 0x02 + db 0x19, 0x18, 0x06, 0x09, 0x05, 0x02, 0x15, 0x09 + db 0x16, 0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 + db 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x02 + db 0x04, 0x08, 0x08, 0x08, 0x04, 0x02, 0x00, 0x0A + db 0x04, 0x1F, 0x04, 0x0A, 0x00, 0x00, 0x04, 0x04 + db 0x1F, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x1F, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 + db 0x06, 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00 + db 0x0E, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0E, 0x04 + db 0x06, 0x04, 0x04, 0x04, 0x04, 0x0E, 0x0E, 0x11 + db 0x10, 0x08, 0x04, 0x02, 0x1F, 0x1F, 0x08, 0x04 + db 0x08, 0x10, 0x11, 0x0E, 0x08, 0x0C, 0x0A, 0x09 + db 0x1F, 0x08, 0x08, 0x1F, 0x01, 0x0F, 0x10, 0x10 + db 0x11, 0x0E, 0x0C, 0x02, 0x01, 0x0F, 0x11, 0x11 + db 0x0E, 0x1F, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02 + db 0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E, 0x0E + db 0x11, 0x11, 0x1E, 0x10, 0x08, 0x06, 0x00, 0x06 + db 0x06, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06 + db 0x00, 0x06, 0x04, 0x02, 0x08, 0x04, 0x02, 0x01 + db 0x02, 0x04, 0x08, 0x00, 0x00, 0x1F, 0x00, 0x1F + db 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04 + db 0x02, 0x0E, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04 + db 0x0E, 0x11, 0x10, 0x16, 0x15, 0x15, 0x0E, 0x04 + db 0x0A, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x0F, 0x11 + db 0x11, 0x0F, 0x11, 0x11, 0x0F, 0x0E, 0x11, 0x01 + db 0x01, 0x01, 0x11, 0x0E, 0x07, 0x09, 0x11, 0x11 + db 0x11, 0x09, 0x07, 0x1F, 0x01, 0x01, 0x0F, 0x01 + db 0x01, 0x1F, 0x1F, 0x01, 0x01, 0x0F, 0x01, 0x01 + db 0x01, 0x0E, 0x11, 0x01, 0x1D, 0x11, 0x11, 0x1E + db 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11, 0x0E + db 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E, 0x1C, 0x08 + db 0x08, 0x08, 0x08, 0x09, 0x06, 0x11, 0x09, 0x05 + db 0x03, 0x05, 0x09, 0x11, 0x01, 0x01, 0x01, 0x01 + db 0x01, 0x01, 0x1F, 0x11, 0x1B, 0x15, 0x15, 0x11 + db 0x11, 0x11, 0x11, 0x11, 0x13, 0x15, 0x19, 0x11 + db 0x11, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E + db 0x0F, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x01, 0x0E + db 0x11, 0x11, 0x11, 0x15, 0x09, 0x16, 0x0F, 0x11 + db 0x11, 0x0F, 0x05, 0x09, 0x11, 0x1E, 0x01, 0x01 + db 0x0E, 0x10, 0x10, 0x0F, 0x1F, 0x04, 0x04, 0x04 + db 0x04, 0x04, 0x04, 0x11, 0x11, 0x11, 0x11, 0x11 + db 0x11, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x0A + db 0x04, 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A + db 0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11, 0x11 + db 0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x1F, 0x10 + db 0x08, 0x04, 0x02, 0x01, 0x1F, 0x0E, 0x02, 0x02 + db 0x02, 0x02, 0x02, 0x0E, 0x00, 0x01, 0x02, 0x04 + db 0x08, 0x10, 0x00, 0x0E, 0x08, 0x08, 0x08, 0x08 + db 0x08, 0x0E, 0x0E, 0x11, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F + db 0x1C, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x0E, 0x10, 0x1E, 0x13, 0x1E, 0x01, 0x01 + db 0x0D, 0x13, 0x11, 0x11, 0x0F, 0x00, 0x00, 0x0E + db 0x01, 0x01, 0x01, 0x0E, 0x10, 0x10, 0x16, 0x19 + db 0x11, 0x11, 0x1E, 0x00, 0x00, 0x0E, 0x11, 0x1F + db 0x01, 0x0E, 0x18, 0x04, 0x04, 0x0E, 0x04, 0x04 + db 0x04, 0x00, 0x0E, 0x11, 0x11, 0x1E, 0x10, 0x0E + db 0x01, 0x01, 0x0D, 0x13, 0x11, 0x11, 0x11, 0x04 + db 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x00 + db 0x08, 0x08, 0x08, 0x08, 0x06, 0x01, 0x01, 0x09 + db 0x05, 0x03, 0x05, 0x09, 0x04, 0x04, 0x04, 0x04 + db 0x04, 0x04, 0x08, 0x00, 0x00, 0x0F, 0x15, 0x15 + db 0x15, 0x15, 0x00, 0x00, 0x09, 0x13, 0x11, 0x11 + db 0x11, 0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E + db 0x00, 0x00, 0x0E, 0x11, 0x11, 0x0F, 0x01, 0x00 + db 0x00, 0x0E, 0x11, 0x11, 0x1E, 0x10, 0x00, 0x00 + db 0x0D, 0x13, 0x01, 0x01, 0x01, 0x00, 0x00, 0x1E + db 0x01, 0x0E, 0x10, 0x0F, 0x04, 0x04, 0x0E, 0x04 + db 0x04, 0x04, 0x18, 0x00, 0x00, 0x11, 0x11, 0x11 + db 0x19, 0x16, 0x00, 0x00, 0x11, 0x11, 0x0A, 0x0A + db 0x04, 0x00, 0x00, 0x11, 0x15, 0x15, 0x15, 0x0A + db 0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x00 + db 0x00, 0x11, 0x11, 0x1E, 0x10, 0x0C, 0x00, 0x00 + db 0x1F, 0x08, 0x04, 0x02, 0x1F, 0x10, 0x08, 0x08 + db 0x04, 0x08, 0x08, 0x10, 0x04, 0x04, 0x04, 0x04 + db 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x10, 0x08 + db 0x08, 0x04, 0x00, 0x02, 0x15, 0x08, 0x00, 0x00 + db 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15 + db 0x00, 0x00, 0x09, 0x15, 0x17, 0x15, 0x09, 0x00 + db 0x00, 0x06, 0x08, 0x0E, 0x09, 0x16, 0x06, 0x01 + db 0x01, 0x06, 0x09, 0x09, 0x06, 0x00, 0x00, 0x09 + db 0x09, 0x09, 0x1F, 0x10, 0x00, 0x00, 0x0E, 0x0A + db 0x0A, 0x1F, 0x11, 0x00, 0x00, 0x0E, 0x11, 0x1F + db 0x01, 0x1E, 0x00, 0x04, 0x0E, 0x15, 0x15, 0x0E + db 0x04, 0x00, 0x00, 0x0F, 0x09, 0x01, 0x01, 0x01 + db 0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x00 + db 0x00, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0A, 0x04 + db 0x11, 0x19, 0x15, 0x13, 0x11, 0x00, 0x00, 0x11 + db 0x09, 0x07, 0x09, 0x11, 0x00, 0x00, 0x1C, 0x12 + db 0x12, 0x12, 0x11, 0x00, 0x00, 0x11, 0x1B, 0x15 + db 0x11, 0x11, 0x00, 0x00, 0x11, 0x11, 0x1F, 0x11 + db 0x11, 0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E + db 0x00, 0x00, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x00 + db 0x00, 0x1E, 0x11, 0x1E, 0x14, 0x12, 0x00, 0x00 + db 0x07, 0x09, 0x07, 0x01, 0x01, 0x00, 0x00, 0x0E + db 0x01, 0x01, 0x01, 0x0E, 0x00, 0x00, 0x1F, 0x04 + db 0x04, 0x04, 0x04, 0x00, 0x00, 0x11, 0x11, 0x1E + db 0x10, 0x0E, 0x00, 0x00, 0x15, 0x15, 0x0E, 0x15 + db 0x15, 0x00, 0x00, 0x03, 0x05, 0x07, 0x09, 0x07 + db 0x00, 0x00, 0x01, 0x01, 0x07, 0x09, 0x07, 0x00 + db 0x00, 0x11, 0x11, 0x13, 0x15, 0x13, 0x00, 0x00 + db 0x0E, 0x11, 0x0C, 0x11, 0x0E, 0x00, 0x00, 0x15 + db 0x15, 0x15, 0x15, 0x1F, 0x00, 0x00, 0x07, 0x08 + db 0x0E, 0x08, 0x07, 0x00, 0x00, 0x15, 0x15, 0x15 + db 0x1F, 0x10, 0x00, 0x00, 0x09, 0x09, 0x0E, 0x08 + db 0x08, 0x00, 0x00, 0x06, 0x05, 0x0C, 0x14, 0x0C + +; 32 chars cp=1 (Russian letters) +m_font_matrix_cp2: + db 0x09, 0x15, 0x15, 0x17, 0x15, 0x15, 0x09, 0x04 + db 0x0A, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x1F, 0x11 + db 0x01, 0x0F, 0x11, 0x11, 0x1F, 0x09, 0x09, 0x09 + db 0x09, 0x09, 0x1F, 0x10, 0x0C, 0x0A, 0x0A, 0x0A + db 0x0A, 0x1F, 0x11, 0x1F, 0x01, 0x01, 0x0F, 0x01 + db 0x01, 0x1F, 0x04, 0x0E, 0x15, 0x15, 0x15, 0x0E + db 0x04, 0x1F, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01 + db 0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11, 0x11 + db 0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x04, 0x15 + db 0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x09, 0x05 + db 0x03, 0x05, 0x09, 0x11, 0x1C, 0x12, 0x12, 0x12 + db 0x12, 0x12, 0x11, 0x11, 0x1B, 0x15, 0x15, 0x11 + db 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11 + db 0x11, 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E + db 0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E + db 0x11, 0x11, 0x11, 0x1E, 0x12, 0x11, 0x0F, 0x11 + db 0x11, 0x11, 0x0F, 0x01, 0x01, 0x0E, 0x11, 0x01 + db 0x01, 0x01, 0x11, 0x0E, 0x1F, 0x04, 0x04, 0x04 + db 0x04, 0x04, 0x04, 0x11, 0x11, 0x11, 0x11, 0x1E + db 0x10, 0x0E, 0x15, 0x15, 0x15, 0x0E, 0x15, 0x15 + db 0x15, 0x0F, 0x11, 0x11, 0x0F, 0x11, 0x11, 0x0F + db 0x01, 0x01, 0x01, 0x0F, 0x11, 0x11, 0x0F, 0x11 + db 0x11, 0x11, 0x13, 0x15, 0x15, 0x13, 0x0E, 0x11 + db 0x10, 0x0C, 0x10, 0x11, 0x0E, 0x15, 0x15, 0x15 + db 0x15, 0x15, 0x15, 0x1F, 0x0E, 0x11, 0x10, 0x1C + db 0x10, 0x11, 0x0E, 0x15, 0x15, 0x15, 0x15, 0x15 + db 0x1F, 0x10, 0x11, 0x11, 0x11, 0x1E, 0x10, 0x10 + db 0x10, 0x1F, 0x15, 0x1F, 0x15, 0x1F, 0x15, 0x1C + +m_print_write: + LD SP, m_font_cp0+28 + ds 24, 0xFF + +m_ramdisk_read: + PUSH HL + PUSH DE + LD A, D + AND 0x1 + OR 0x2 + LD B, A + XOR A + LD A, E + RRA + LD D, A + LD A, 0x0 + RRA + LD E, A +LAB_ram_f3ee: + LD A,B + OUT (SYS_DD17PB), A + LD A, (DE) + LD C, A + XOR A + OUT (SYS_DD17PB), A + LD (HL), C + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, LAB_ram_f3ee + XOR A + OUT (SYS_DD17PB), A + POP DE + POP HL + RET +m_ramdisk_write: + PUSH HL + PUSH DE + LD A, D + AND 0x1 + OR 0x2 + LD B, A + XOR A + LD A, E + RRA + LD D, A + LD A, 0x0 + RRA + LD E, A +LAB_ram_f414: + XOR A + OUT (SYS_DD17PB), A + LD C, (HL) + LD A,B + OUT (SYS_DD17PB), A + LD A, C + LD (DE), A + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, LAB_ram_f414 + XOR A + OUT (SYS_DD17PB), A + POP DE + POP HL + RET + +; ------------------------------------------------------ +; Write block to Tape +; In: DE - block ID, +; HL -> block of data. +; ------------------------------------------------------ +m_tape_write: + PUSH HL + PUSH DE + PUSH DE + LD BC,2550 + LD A,PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A,TMR0_SQWAVE ; tmr0, load lsb+msb, sq wave, bin + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A,B + OUT (TMR_DD70C1), A + ; Write Hi+Lo, Hi+Lo + LD DE, 0x4 ; repeat next 4 times +tw_wait_t0_l1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; check rst4 from timer#0 + JP NZ,tw_wait_t0_l1 + LD A, D + CPL + LD D, A + OR A + LD A,TL_HIGH ; tape level hi + JP NZ,tw_set_tape_lvl + LD A,TL_LOW ; tape level low +tw_set_tape_lvl: + OUT (DD67PC), A ; set tape level + LD A,TMR0_SQWAVE ; tmr0, load lsb+msb, swq, bin + ; timer on + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A,B + OUT (TMR_DD70C1), A + DEC E + JP NZ,tw_wait_t0_l1 +tw_wait_t0_l2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ,tw_wait_t0_l2 + ; Write 00 at start + LD A, 0x0 + CALL m_tape_wr_byte + ; Write 0xF5 marker + LD A, 0xf5 + CALL m_tape_wr_byte + LD E, 0x0 ; checksum=0 + ; Write block ID + POP BC + LD A, C + CALL m_tape_wr_byte + LD A,B + CALL m_tape_wr_byte + ; Write 128 data bytes + LD B,128 +tw_wr_next_byte: + LD A, (HL) + CALL m_tape_wr_byte + INC HL + DEC B + JP NZ,tw_wr_next_byte + ; Write checksum + LD A, E + CALL m_tape_wr_byte + ; Write final zero byte + LD A, 0x0 + CALL m_tape_wr_byte +tw_wait_t0_l3: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ,tw_wait_t0_l3 + LD A,TL_MID ; tape level middle + OUT (DD67PC), A + POP DE + POP HL + RET +; ------------------------------------------------------ +; Write byte to tape +; Inp: A - byte top write +; D - current level +; E - current checksum +; ------------------------------------------------------ +m_tape_wr_byte: + PUSH BC +; calc checksum + LD B, A + LD A, E + SUB B + LD E, A + LD C,8 ; 8 bit in byte +twb_get_bit: + LD A,B + RRA + LD B, A + JP C,twb_bit_hi +twb_wait_t0_l1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ,twb_wait_t0_l1 + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; program for 360 cycles + LD A, 0x68 + OUT (TMR_DD70C1), A + LD A, 0x1 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A,TL_HIGH + JP NZ,twb_out_bit_l1 + LD A,TL_LOW +twb_out_bit_l1: + OUT (DD67PC), A + DEC C + JP NZ,twb_get_bit + POP BC + RET +twb_bit_hi: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ,twb_bit_hi + ; program for 660 cycles + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + LD A, 0x94 + OUT (TMR_DD70C1), A + LD A, 0x2 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A,TL_HIGH + JP NZ,twb_out_bit_l2 + LD A,TL_LOW +twb_out_bit_l2: + OUT (DD67PC), A + DEC C + JP NZ,twb_get_bit + POP BC + RET + +; ------------------------------------------------------ +; Load block from Tape +; In: HL -> buffer to receive bytes from Tape +; Out: A = 0 - ok, +; 1 - CRC error, +; 2 - unexpected block Id +; 4 - key pressed +; ------------------------------------------------------ +m_tape_read: + PUSH HL + PUSH DE + LD A,PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A ; tmr0, load lsb+msb, sq wave + LD A, 0x0 + ; tmr0 load 0x0000 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + LD C,3 +tr_wait_3_changes: + CALL ccp_read_tape_bit_kbd + INC A + JP Z,tr_key_pressed_l0 + LD A,B + ADD A,4 + JP P,tr_wait_3_changes + DEC C + JP NZ,tr_wait_3_changes +tr_wait_4th_change: + CALL ccp_read_tape_bit_kbd + INC A + JP Z,tr_key_pressed_l0 + LD A,B + ADD A,4 + JP M,tr_wait_4th_change + LD C, 0x0 +tr_wait_f5_marker: + CALL ccp_read_tape_bit_kbd + INC A + JP Z,tr_key_pressed_l0 + DEC A + RRA + LD A, C + RRA + LD C, A + CP 0xf5 + JP NZ,tr_wait_f5_marker + LD E, 0x0 ; checksum = 0 + ; Read blk ID + CALL m_tape_read_byte + JP NC,tr_err_read_id + LD C, D + CALL m_tape_read_byte + JP NC,tr_err_read_id + LD B, D + PUSH BC + ; Read block, 128 bytes + LD C,128 +tr_read_next_b: + CALL m_tape_read_byte + JP NC,tr_err_read_blk + LD (HL), D + INC HL + DEC C + JP NZ,tr_read_next_b + + ; Read checksum + CALL m_tape_read_byte + JP NC,tr_err_read_blk + LD A, E + OR A + JP Z,tr_checksum_ok + LD A, 0x1 ; bad checksum +tr_checksum_ok: + POP BC +tr_return: + POP DE + POP HL + RET +tr_err_read_blk: + POP BC + LD BC, 0x0 +tr_err_read_id: + LD A, 0x2 ; read error + JP tr_return +tr_key_pressed_l0: + CALL tm_con_in + LD C, A ; store key code in C + LD B, 0x0 + LD A, 0x4 + JP tr_return +m_tape_read_byte: + PUSH BC + +; ------------------------------------------------------ +; Read byte from Tape +; Out: D - byte +; CF is set if ok, cleared if error +; ------------------------------------------------------ + LD C,8 +trb_next_bit: + CALL ccp_read_tape_bit + ; push bit from lo to hi in D + RRA + LD A, D + RRA + LD D, A + LD A,4 + ADD A,B + JP NC,trb_ret_err + DEC C + JP NZ,trb_next_bit + ; calc checksum + LD A, D + ADD A, E + LD E, A + SCF +trb_ret_err: + POP BC + RET + +; ------------------------------------------------------ +; Read bit from tape +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +ccp_read_tape_bit: + IN A, (KBD_DD78PB) ; Read Tape bit 5 (data) + AND TAPE_P + LD B, A +rtb_wait_change: + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z,rtb_wait_change + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; [360...480...660] 0x220=544d + IN A, (TMR_DD70C1) ; get tmer#0 lsb + ADD A, 0x20 + IN A, (TMR_DD70C1) ; get tmer#0 msb + LD B, A + ADC A, 0x2 + ; reset timer to 0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; For 0 - 65535-360+544 -> overflow P/V=1 + ; For 1 - 65535-660+544 -> no overflow P/V=0 + RET P + INC A + RET + +; ------------------------------------------------------ +; Read bit from tape with keyboard interruption +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +ccp_read_tape_bit_kbd: + IN A, (KBD_DD78PB) + AND TAPE_P + LD B, A ; save tape bit state + ; wait change with keyboard check +twc_wait_change: + IN A, (PIC_DD75RS) + AND KBD_IRQ + JP NZ,twc_key_pressed + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z,twc_wait_change + ; measure time + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; read lsb+msb + IN A, (TMR_DD70C1) + ADD A, 0x20 + IN A, (TMR_DD70C1) + LD B, A + ADC A, 0x2 + ; reset timer#0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; flag P/V is set for 0 + RET P + INC A + RET +twc_key_pressed: + LD A, 0xff + RET + +; ------------------------------------------------------ +; Wait tape block +; Inp: A - periods to wait +; Out: A=4 - interrupded by keyboard, C=key +; ------------------------------------------------------ +m_tape_wait: + OR A + RET Z + PUSH DE + LD B, A +m_wait_t4: + LD C,B + IN A, (KBD_DD78PB) + AND TAPE_P ; Get TAPE4 (Wait det) and save + LD E, A ; store T4 state to E +m_wait_next_2ms: + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A +; load 3072 = 2ms + XOR A + OUT (TMR_DD70C1), A + LD A, 0xc + OUT (TMR_DD70C1), A +m_wait_tmr_key: + IN A, (PIC_DD75RS) + AND KBD_IRQ ; RST1 flag (keyboard) + JP NZ,mt_key_pressed + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; RST4 flag (timer out) + JP Z,m_wait_no_rst4 + IN A, (KBD_DD78PB) + AND TAPE_P ; TAPE4 not changed? + CP E + JP NZ,m_wait_t4 ; continue wait + JP m_wait_tmr_key +m_wait_no_rst4: + DEC C + JP NZ,m_wait_next_2ms + XOR A + POP DE + RET +mt_key_pressed: + CALL tm_con_in + LD C, A ; C = key pressed + LD A, 0x4 ; a=4 interrupted by key + POP DE + RET + +; ------------------------------------------------------ +; Check block marker from Tape +; Out: A=0 - not detected, 0xff - detected +; ------------------------------------------------------ +m_tape_blk_detect: + IN A, (KBD_DD78PB) + AND TAPE_D ; TAPE5 - Pause detector + LD A, 0x0 + RET Z + CPL + RET + +esc_64x23_cursor: + db ASCII_ESC, '6', '1', ASCII_ESC, '8', '0', 0 + +msg_turbo_mon: + db "\f\r\n Turbo MONITOR Ver 1.1 for 13.01.92 (C)Alex Z.\r\n",0 + +m_rst1_handler: + DI + LD (TM_VARS.rst_hl_save), HL + LD HL, 0x2 + ADD HL,SP + LD (TM_VARS.rst_sp_save), HL + POP HL + LD SP, TM_VARS.rst_hl_save + PUSH DE + PUSH BC + PUSH AF + LD (TM_VARS.rst_ret_addr), HL + LD (TM_VARS.tm_hrg), HL + JP tm_main +tm_rst_ret: + LD A, 0xc3 + LD (TM_VARS.rst_ret_JP), A + LD SP, TM_VARS.rst_af_save + POP AF + POP BC + POP DE + POP HL + LD SP, HL + LD HL, (TM_VARS.rst_hl_save) + JP TM_VARS.rst_ret_JP + +m_cold_start: + LD HL, tpa_start + LD (TM_VARS.tm_hrg), HL +tm_main: + LD SP, TM_VARS.tm_stsp + LD DE,esc_64x23_cursor ; FORM61 + CALL tm_print + LD DE, msg_turbo_mon ;= '\f' + CALL tm_out_strz + LD DE, 0x363d ; 54 x '=' + CALL tm_rpt_print + CALL tm_beeep + +tm_mon: + LD DE, 0x300 + CALL tm_screen + CALL tm_lps + LD DE, 0x520 + CALL tm_rpt_print + LD HL, msg_digits ;= "0123456789ABCDEF" + + ; Out '012345789ABCDEF' string +tm_tit: + CALL tm_print_SP + LD C, (HL) + CALL tm_sym_out + INC HL + LD A, (HL) + OR A + JP NZ, tm_tit + LD HL, (TM_VARS.tm_hrg) + LD L, 0x0 +tm_adr: + CALL tm_print_LFCR_hexw + +tm_prh: + LD A, (HL) + CALL tm_hex_b + INC L + LD A,L + AND 0xf + CP 0x0 + JP NZ, tm_prh + LD A, L + SUB 0x10 + LD L, A + CALL tm_print_SP + CALL tm_buk + LD A, L + CP 0x0 + JP NZ, tm_adr + CALL tm_print_LFCR + CALL tm_lpr + LD DE, 0x363d ; 54 x '=' + CALL tm_rpt_print + CALL tm_print_LFCR + LD DE, tm_msg_regs ;= "A,FB,CD,EH,L SP" + LD HL, TM_VARS.rst_af_save + +LAB_ram_f717: + LD B, 0x3 + +LAB_ram_f719: + LD A, (DE) ;= "A,FB,CD,EH,L SP" + LD C, A + CP 0xff + JP Z,tm_out_contacts + CP 0x0 + JP Z, LAB_ram_f744 + CALL tm_sym_out + INC DE + DEC B + JP NZ, LAB_ram_f719 + LD C,'=' + CALL tm_sym_out + INC HL + LD A, (HL) + CALL tm_hex_b + DEC HL + LD A, (HL) + CALL tm_hex_b + INC HL + INC HL + CALL tm_print_SP + JP LAB_ram_f717 +LAB_ram_f744: + INC HL + INC DE + JP LAB_ram_f719 + +tm_msg_regs: + db "A,FB,CD,EH,L SP",0 + db " PC", 0xFF + +tm_out_contacts: + LD DE, m_msg_contacts ;= " (Chernogolovka Mosk.reg. ... + CALL tm_out_strz + LD HL, (TM_VARS.tm_hrg) +tm_kur: + LD DE, 0x405 + LD A, L + AND 0xf0 + RRCA + RRCA + RRCA + RRCA + ADD A, D + LD D, A + LD A, L + AND 0xf + ADD A, A + ADD A, E + LD E, A + CALL tm_screen + LD A, (TM_VARS.tm_tbu) + CP 0x9 + JP Z, tm_tabu + LD B, 0x1 +tm_m2: + CALL tm_con_in + LD C, A + CP 0x3 + JP Z, m_print_log_sep + CALL tm_poke + LD A, B + OR A + JP NZ, tm_m6 + CALL tm_re1 + JP tm_m2 +tm_m6: + LD A, C + CP 0x9 + JP NZ, tm_m7 + LD (TM_VARS.tm_tbu), A + JP tm_kur +tm_tabu: + CALL tm_print_CR + LD E, '&' + LD A, L + AND 0xf + ADD A, E + LD E, A + CALL tm_scr1 +tm_tab: + CALL tm_get_key_ctrl_c + CP ASCII_TAB + JP NZ, tm_m7 + XOR A + LD (TM_VARS.tm_tbu), A + JP tm_kur +tm_m7: + CP 0x8b + JP Z, tm_search + CP 0x9e + JP Z, tm_f1 + CP 0x81 + JP Z, tm_f2 + CP 0x86 + JP Z, tm_f3 + CP 0x92 + JP Z, tm_f4 + CP 0x83 + JP Z, tm_f5 + LD A, (TM_VARS.tm_tbu) + CP 0x9 + LD A, C + JP NZ, tm_m8_monitor + CALL tm_m9 + JP tm_tab +tm_m9: + CP 0x93 + JP Z, tm_leta + CP 0x8 + JP Z, tm_leta + CP 0x84 + JP Z, tm_rgta + CP 0x85 + JP Z, tm_up + CP 0x98 + JP Z, tm_down + AND 0x7f + CP 0x20 + LD (HL), C + JP NC, tm_met1 + LD C, 0x20 + CP ASCII_CR + JP NZ, tm_met1 + CALL tm_sym_out + CALL tm_rigth1 + LD (HL), ASCII_LF + LD C, 0x20 +tm_met1: + CALL tm_sym_out + JP tm_rigth1 +tm_leta: + LD A, L + OR A + RET Z + AND 0xf + JP NZ, tm_left1 + LD C, 0x19 ; EM (End of medium) + CALL tm_sym_out + LD DE, 0x1018 ; 16 x CAN (Cancel) + CALL tm_rpt_print +tm_left1: + DEC L + LD C, 0x8 ; BS (Backspace) + JP tm_sym_out +tm_rgta: + LD C, 0x18 ; CAN + CALL tm_sym_out +tm_rigth1: + INC L + JP Z, tm_left1 +tm_rigth2: + LD A, L + AND 0xf + RET NZ + LD DE, 0x1008 ; 16 x BS + CALL tm_rpt_print + LD C, ASCII_LF + JP tm_sym_out +tm_m8_monitor: + CP ASCII_ESC + JP NZ, tm_m3 + CALL tm_niz + JP tm_rst_ret +tm_m3: + CP ASCII_CR + JP Z, tm_main + CP 0x99 + JP Z, tm_fill + CP 0x7f + JP Z, tm_fill_nxt2 + CP '+' + JP Z, tm_plus + CP '-' + JP Z, tm_minus + CP 'G' + JP NZ, tm_rstr + LD DE, tm_main + PUSH DE + JP (HL) +tm_rstr: + CP 'J' + JP Z, tm_jump + CP 'H' + JP Z, tm_add + CP 'M' + JP Z, tm_move + CP 'R' + JP Z, tm_dsk_read + CP 'W' + JP Z, tm_dsk_write + CP 'T' + JP Z, tm_print_t + CP 'P' + LD (TM_VARS.tm_hrg), HL + JP Z, LAST ; ??? + CP 'p' + JP Z, tm_lprn + CP 0x10 + ; if key 0x10 - turn on duplicate output to printer + JP Z, tm_ltab + CALL tm_met2 + LD (TM_VARS.tm_hrg), HL + JP tm_m2 +tm_met2: + CP 0x93 + JP Z, tm_left + CP 0x8 + JP Z, tm_left + CP 0x84 + JP Z, tm_rght + CP 0x85 + JP Z, tm_up + CP 0x98 + JP Z, tm_down + RET +tm_left: + LD A,B + CP 0x2 + JP Z, tm_le2 + LD A,L + AND 0xf + RET Z +tm_le3: + DEC L + LD B, 0x2 +tm_le1: + LD C, ASCII_BS + JP tm_sym_out +tm_le2: + LD B, 0x1 + JP tm_le1 +tm_rght: + LD C, 0x18 + CALL tm_sym_out + LD A, B + CP 0x1 + LD B, 0x2 + RET Z +tm_re1: + INC L + JP Z, tm_le3 + LD A, L + AND 0xf + LD B, 0x1 + JP Z, tm_print_LFCR_hexw + RET +tm_up: + LD A, L + AND 0xf0 + RET Z + LD A, L + SUB 0x10 + LD L, A + LD C, 0x19 + JP tm_sym_out +tm_down: + LD A, L + ADD A, 0x10 + AND 0xf0 + RET Z + LD A, 0x10 + ADD A, L + LD L, A + LD C, 0xa + JP tm_sym_out + +; ------------------------------------------------------ +; Get nibbles from A and (HL), conver to byte +; put back to (HL), print nibble +; ------------------------------------------------------ +tm_poke: + CP 'G' + RET P + CP '0' + RET M + SUB '0' + CP 10 + JP M, p_is_alpha + SUB 0x7 +p_is_alpha: + PUSH AF + CALL tm_print_hex_nibble + LD A, B + CP 0x2 + JP Z, tm_m5 + LD A, (HL) + AND 0xf + LD B, A + POP AF + RLCA + RLCA + RLCA + RLCA + ADD A, B + LD (HL), A + LD B, 0x2 + RET + +tm_m5: + LD A, (HL) + AND 0xf0 + LD B, A + POP AF + ADD A, B + LD (HL), A + XOR A + LD B, A + RET + +tm_pokh: + CALL tm_pok + DEC HL +tm_pok: + LD B, 0x1 +tm_pk1: + CALL tm_get_key_ctrl_c + CALL tm_poke + LD A, B + OR A + JP NZ, tm_pk1 + RET +tm_buk: + LD A, (HL) + LD C, A + AND 0x7f + CP 0x20 + JP P, tm_m24 + LD C, 0x20 +tm_m24: + CALL tm_sym_out + INC L + LD A, L + AND 0xf + CP 0x0 + JP NZ, tm_buk + RET +m_msg_jump: + db "JAMP\r\n", 0 + +tm_jump: + LD DE, 0x300 + CALL tm_screen + LD DE, m_msg_jump ;= "JAMP\r\n" + CALL tm_print + LD HL, TM_VARS.tm_hrg+1 + CALL tm_pokh + JP tm_mon +m_msg_add: + db "\fADD [", 0 + +tm_add: + LD DE,m_msg_add ;= "\fADD [" + CALL tm_print + LD HL, TM_VARS.tm_strt+1 + CALL tm_pokh + LD C,'+' + CALL tm_sym_out + LD HL, TM_VARS.tm_strt+3 + CALL tm_pokh + LD C,'=' + CALL tm_sym_out + LD HL, (TM_VARS.tm_strt) + EX DE, HL + LD HL, (TM_VARS.tm_strt+2) + ADD HL, DE + CALL tm_hex_hl + CALL tm_con_in + JP tm_main + +m_msg_move: + db "\fMOVE }", 0 + +tm_move: + LD DE,m_msg_move ;= "\fMOVE }" + CALL tm_print + LD HL, TM_VARS.tm_strt+1 + CALL tm_pokh + LD C,'-' + CALL tm_sym_out + LD HL, TM_VARS.tm_strt+3 + CALL tm_pokh + LD C,'=' + CALL tm_sym_out + LD HL, TM_VARS.tm_strt+5 + CALL tm_pokh + LD HL, (TM_VARS.tm_strt) + LD B,H + LD C,L + LD HL, (TM_VARS.tm_strt+2) + LD D,H + LD E,L + INC DE + LD HL, (TM_VARS.tm_strt+4) + +tm_mov_nxt: + LD A, (BC) + LD (HL), A + INC BC + INC HL + LD A, C + CP E + JP NZ, tm_mov_nxt + LD A, B + CP D + JP NZ, tm_mov_nxt + JP tm_main +tm_dsk_read: + LD DE, m_msg_read ;= "Read/" + JP tm_ra4 +tm_dsk_write: + LD DE, m_msg_write ;= "Write/" +tm_ra4: + LD (TM_VARS.tm_rw_disk), A + CALL tm_print_verh + CALL tm_print +tm_ra5: + CALL tm_get_key_ctrl_c + CP 'A' + JP C, tm_ra5 + CP 'F' + JP NC, tm_ra5 + LD (TM_VARS.tm_drive), A + CALL tm_sym_out + LD C, ':' + CALL tm_sym_out + LD A, (TM_VARS.tm_drive) + SUB 65 + LD (TM_VARS.tm_drive), A + JP NZ, tm_disk + LD DE, tm_msg_0123 ;= "0123 ?" + CALL tm_print +tm_rrr: + CALL tm_get_key_ctrl_c + SUB 48 + CP 4 + JP NC, tm_rrr + LD (TM_VARS.tm_strt+15), A + JP tm_main +tm_beeep: + LD BC, 0x6060 +tm_bell_next_p: + LD A,BELL_PIN + OUT (DD67PC), A ; BELL=1 + LD A, C +tm_bell_w1: + DEC A + JP NZ, tm_bell_w1 + OUT (DD67PC), A ; BELL 0 + LD A, C +tm_bell_w2: + DEC A + JP NZ, tm_bell_w2 + DEC B + JP NZ, tm_bell_next_p + RET +tm_msg_0123: + db "0123 ?", 0 + +tm_disk: + LD DE, m_msg_track ;= "/Track-" + CALL tm_print + CALL tm_des_dv + LD (TM_VARS.tm_track_no), A + LD DE, tm_sector ;= "/Sekt-" + CALL tm_print + CALL tm_des_dv + LD (TM_VARS.tm_sector_no), A + LD DE, m_msg_num ;= "/N-" + CALL tm_print + CALL tm_des_dv + LD (TM_VARS.tm_num_sect), A + LD DE, m_msg_io ;= "/(I/O)-" + CALL tm_print +tm_disk_op_unk: + CALL tm_get_key_ctrl_c + CP 'I' + JP Z, tm_disk_inp + CP 'O' + JP NZ, tm_disk_op_unk +tm_disk_inp: + LD (TM_VARS.tm_disk_op), A + CALL tm_sym_out + CALL tm_rd_wr_disk + JP tm_cont +tm_rd_wr_disk: + CALL tm_niz +tm_rd: + CALL tm_print_CR + LD A, (TM_VARS.tm_track_no) + LD D, A + LD A, (TM_VARS.tm_sector_no) + LD E, A + LD A, (TM_VARS.tm_disk_op) + CP 'O' + CALL Z, tm_sect_map + LD A, H + CP 0xac + JP NC, tm_error + LD BC, 0x84 + LD A, (TM_VARS.tm_rw_disk) + CP 'W' + LD A, (TM_VARS.tm_drive) + JP NZ, tm_d3 + LD C, 0xa4 + CALL m_write_floppy + DEC L + JP tm_d4 +tm_d3: + CALL m_read_floppy +tm_d4: + CP 0x0 + JP NZ, tm_error + LD A, (TM_VARS.tm_track_no) + LD D, A + LD A, (TM_VARS.tm_sector_no) + INC A + CP 10 + JP C, tm_wf_nx_trk + INC D + LD A, D + LD (TM_VARS.tm_track_no), A + LD A, 0x1 +tm_wf_nx_trk: + LD (TM_VARS.tm_sector_no), A + LD E, A + CALL tm_print_CR_hexw + CALL tm_dv_des + LD A, (TM_VARS.tm_num_sect) + DEC A + LD (TM_VARS.tm_num_sect), A + JP NZ, tm_rd + RET +tm_error: + PUSH AF + LD DE, m_msg_disk_error ;= "Disk Error - " + CALL tm_print + POP AF + CALL tm_hex_b + LD BC, 0x70ff + CALL tm_bell_next_p + LD DE, m_msg_track ;= "/Track-" + CALL tm_print + LD A, (TM_VARS.tm_track_no) + CALL tm_dva + LD DE, tm_sector ;= "/Sekt-" + CALL tm_print + LD A, (TM_VARS.tm_sector_no) + CALL tm_dva + LD DE, m_msg_retr_abrt ;= "Retry, Abort -?" + CALL tm_print_w_key + CP 'A' + JP Z, tm_main + JP tm_rd_wr_disk +tm_dv_des: + CALL tm_dv1 + LD A, E + +; ------------------------------------------------------ +; Convert to BCD and print? +; ------------------------------------------------------ +tm_dva: + LD D, A +tm_dv1: + LD C, 0 + LD B, 8 +tm_dv2: + LD A, D + RLCA + LD D, A + LD A, C + ADC A, C + DAA + LD C, A + DEC B + JP NZ, tm_dv2 + +OFF_ram_fb76: + JP tm_hex_b + +tm_sec_map_t: + db 1, 8, 6, 4, 2, 9, 7, 5, 3 + +; ------------------------------------------------------ +; Map sectors +; Inp: E - sector +; Out: E - sector +; ------------------------------------------------------ +tm_sect_map: + LD BC, tm_sec_map_t-1 + LD A, E + ADD A, C + LD C, A + JP NC, tm_tbl_noc + INC B +tm_tbl_noc: + LD A, (BC) + LD E, A + RET +tm_des_dv: + CALL SUB_ram_fb9f + RLCA + RLCA + RLCA + LD B, A + RRCA + RRCA + ADD A,B + LD B, A + CALL SUB_ram_fb9f + ADD A,B + RET +SUB_ram_fb9f: + CALL tm_get_key_ctrl_c + SUB 0x30 + JP M,SUB_ram_fb9f + CP 0xa + JP P,SUB_ram_fb9f + PUSH AF + CALL tm_sym_out + POP AF + RET + +; ------------------------------------------------------ +; Read data from floppy +; Inp: A = 0..4 - select drives +; HL -> buffer +; C - command (0x84) READ_SEC, single, 15ms delay +; Out: A=0 if ok +; CF is set if error +; ------------------------------------------------------ +m_read_floppy: + CALL m_select_drive + CALL m_start_floppy + RET C + CALL m_fdc_seek_trk + RET C + CALL m_fdc_read_c_bytes + RET C + XOR A + RET + +; ------------------------------------------------------ +; Write data to floppy +; Inp: A = 0..4 - select drives +; HL -> buffer +; C - command (0xA4) WRITE_SEC, single, 15ms delay +; Out: A=0 if ok +; CF is set if error +; ------------------------------------------------------ +m_write_floppy: + CALL m_select_drive + CALL m_start_floppy + RET C + CALL m_fdc_seek_trk + RET C + CALL m_fdc_write_bytes + RET C + XOR A + RET + +; ------------------------------------------------------ +; Select or disable drive +; Inp: A = 0 - disable drives +; A = 1 - select + drsel + motor0 en +; A = 2 - select + motor0 enanle +; A = 3 - select +; A = 4 - select + drsel +; ------------------------------------------------------ +m_select_drive: + PUSH BC + LD B, 00000111b ; DRSEL, DSEL1, DSEL0 + CP 0x1 + JP Z, mal_out_to_flop + LD B, 00100111b ; MOT0_EN, DRSEL, DSEL1, DSEL0 + CP 0x2 + JP Z, mal_out_to_flop + LD B, 00000011b ; DSEL1, DSEL0 + CP 0x3 + JP Z, mal_out_to_flop + LD B, 00100011b ; MOT0_EN, DSEL1, DSEL0 + CP 0x4 + JP Z, mal_out_to_flop + LD B, 00000000b ; Turn off and deselect drives +mal_out_to_flop: + LD A, B + OUT (FLOPPY), A + POP BC + RET + +; ------------------------------------------------------ +; Start floppy +; Out: +; CF is set, A=0x20 if INTRQ +; CF not set, A=0x00 if READY +; ------------------------------------------------------ +m_start_floppy: + IN A, (FLOPPY) ; Get floppy ctrl state + RLCA + JP C, .start_motor + IN A, (FDC_CMD) + AND FDC_NOT_READY + RET Z + +.start_motor: + PUSH BC + LD BC, 64000 ; timeout + CALL m_fdc_set_init + +.wait_mot_start: + IN A, (FDC_CMD) + AND FDC_NOT_READY + JP Z, .delay_for_spin_up + IN A, (FLOPPY) + ; Check motor start bit + RLCA + JP NC, .wait_mot_start + ; Time is out or not started + LD A, 0x20 + JP .ok_exit + ; Delay for spindle spin-up +.delay_for_spin_up: + DEC C + JP NZ, .delay_for_spin_up + DEC B + JP NZ, .delay_for_spin_up + XOR A +.ok_exit: + POP BC + RET + +; ------------------------------------------------------ +; Send INIT to FDC +; ------------------------------------------------------ +m_fdc_set_init: + IN A, (FLOPPY) + AND 01001110b ; Read SSEL, DDEN, MOT1, MOT0 + RRA + OUT (FLOPPY), A ; SSEL DRSEL MOT1 MOT0 + OR 00001000b ; INIT + OUT (FLOPPY), A + RET + +; ------------------------------------------------------ +; Seek track on floppy +; Inp: DE - track/sector +; ------------------------------------------------------ +m_fdc_seek_trk: + PUSH BC + LD B, 0x2 ; try 2 times if error +fs_seek_again: + LD A, D + OUT (FDC_DATA), A ; Set track to search + LD A,FDC_SEEK_LV ; Seek, Load Head, Verify on dst track + OUT (FDC_CMD), A + NOP + NOP + IN A, (FDC_WAIT) + IN A, (FDC_CMD) + AND 00011001b ; SEEK error, CRC error, BUSY flags + CP 0x0 + JP Z, fs_seek_ok + LD A, 0x20 + SCF ; set erro flag + DEC B + JP Z, fs_seek_ok + LD A, FDC_RESTORE_L ; restore with load head + OUT (FDC_CMD), A + NOP + NOP + IN A, (FDC_WAIT) + JP fs_seek_again +fs_seek_ok: + PUSH AF + LD A, E + OUT (FDC_SECT), A + POP AF + POP BC + RET + +; ------------------------------------------------------ +; Write bytes to FDC +; Inp: C - count of bytes +; HL -> buffer +; Out: CF set if error, +; C - error code if CF is set +; ------------------------------------------------------ +m_fdc_write_bytes: + LD A, C + OUT (FDC_CMD), A +fw_next_byte: + IN A, (FDC_WAIT) + RRCA + LD A, (HL) + OUT (FDC_DATA), A + INC HL + JP C,fw_next_byte + JP m_floppy_chk_error + +; ------------------------------------------------------ +; Read bytes from FDC +; Inp: C - command code for VG93 +; HL -> buffer +; Out: CF set if error, +; C - error code if CF is set +; ------------------------------------------------------ +m_fdc_read_c_bytes: + LD A, C + OUT (FDC_CMD), A + JP .skip_first_inc +.read_next_byte: + LD (HL), A + INC HL +.skip_first_inc: + IN A, (FDC_WAIT) + RRCA + IN A, (FDC_DATA) + JP C, .read_next_byte + +m_floppy_chk_error: + IN A, (FDC_CMD) + AND 11011111b ; mask 5th bit Write fault... ??? + CP 0x0 + RET Z ; retutn in no errors + IN A, (FDC_CMD) + LD C, A + SCF + RET + +m_msg_disk_error: + db "Disk Error - ", 0 + +m_msg_retr_abrt: + db "Retry, Abort -?", 0 + +m_msg_read: + db "Read/", 0 + +m_msg_write: + db "Write/", 0 + +m_msg_track: + db "/Track-", 0 + +tm_sector: + db "/Sekt-", 0 + +m_msg_num: + db "/N-", 0 + +m_msg_b_ok: + db " {OK}", 0 + +m_msg_io: + db "/(I/O)-", 0 + +esc_cmd_kod: + db ASCII_ESC, '@', ASCII_ESC, 'R', 3, ESC_CMD_END + +tm_fill: + LD C, (HL) + LD A, 0xb0 +.fill_nxt1: + LD (HL), C + INC HL + CP H + JP NZ, .fill_nxt1 + JP tm_mon +tm_fill_nxt2: + LD C, (HL) +.fill_nxt3: + LD (HL), C + INC L + JP NZ, .fill_nxt3 + JP tm_mon + EX DE, HL + CALL tm_hex_hl + EX DE, HL + RET + +; ------------------------------------------------------ +; Print word as hex +; Inp: HL - value to print +; ------------------------------------------------------ +tm_hex_hl: + LD A, H + CALL tm_hex_b + LD A, L + +; ------------------------------------------------------ +; Print byte in HEX +; Inp: A - Byte to print +; ------------------------------------------------------ +tm_hex_b: + PUSH AF + AND 0xf0 + RRCA + RRCA + RRCA + RRCA + CALL tm_print_hex_nibble + POP AF + PUSH AF + AND 0xf + CALL tm_print_hex_nibble + POP AF + RET + +tm_print_hex_nibble: + PUSH DE + LD DE,msg_digits ;= "0123456789ABCDEF" + ADD A, E + JP NC, .no_cf + INC D +.no_cf: + LD E, A + LD A, (DE) + LD C, A + CALL tm_sym_out + POP DE + RET + +msg_digits: + db "0123456789ABCDEF", 0 + +tm_print_LFCR_hexw: + LD C, ASCII_LF + CALL tm_sym_out + +tm_print_CR_hexw: + CALL tm_print_CR + CALL tm_hex_hl + +tm_print_SP: + LD C, ' ' + JP tm_sym_out + +tm_print_LFCR: + LD C, ASCII_LF + CALL tm_sym_out + +tm_print_CR: + LD C, ASCII_CR + JP tm_sym_out + +; ------------------------------------------------------ +; Get key from keyboard. Return to monitor +; if Ctrl+C pressed +; Out: C=A - key +; ------------------------------------------------------ +tm_get_key_ctrl_c: + CALL tm_con_in + LD C, A + CP 0x3 + JP Z, tm_main + RET + +; TYPE +tm_print_t: + LD C, ASCII_US + CALL tm_sym_out + EX DE, HL + CALL tm_print +tm_cont: + LD DE, m_msg_b_ok ;= " {OK}" + CALL tm_print_w_key + JP tm_main + +; ------------------------------------------------------ +; Print message and wait key +; Ctrl+C to cansel current op and +; return to monitor +; Inp: DE -> strz +; Out: A - key +; ------------------------------------------------------ +tm_print_w_key: + CALL tm_out_strz + JP tm_get_key_ctrl_c + +tm_out_strz: + CALL tm_print_LFCR +tm_print: + LD B,16 +p11_print_next: + LD A, (DE) + OR A + RET Z + CP ASCII_CR + JP NZ, p11_no_CR + DEC B + JP NZ, p11_no_CR + CALL tm_get_key_ctrl_c + LD B, 16 + LD A, (DE) +p11_no_CR: + LD C, A + CALL tm_sym_out + INC DE + JP p11_print_next + +; ------------------------------------------------------ +; Out symbol +; Inp: C - symbol +; ------------------------------------------------------ +tm_sym_out: + LD A, C + AND 0x80 + JP Z, tm_rus + LD A, 0x1 + +tm_rus: + LD (TM_VARS.m_codepage), A + CALL m_con_out + LD A, (TM_VARS.tm_ltb+1) ; if 0x10, return, no out to printer; + CP 0x10 + RET NZ + JP tm_char_print + +; ------------------------------------------------------ +; Print FS (FileSeparator) +; ------------------------------------------------------ +tm_print_verh: + LD C, 0xc + JP tm_sym_out +tm_screen: + CALL tm_print_verh + LD A, D + OR A +tm_scr0: + JP Z,tm_scr1 + LD C, ASCII_LF + CALL tm_sym_out + DEC D + JP tm_scr0 + +; ------------------------------------------------------ +; Print CAN symbol E times +; ------------------------------------------------------ +tm_scr1: + LD A, E + OR A + RET Z + LD C, ASCII_CAN ; CAN (Cancel) + CALL tm_sym_out + DEC E + JP tm_scr1 + +tm_niz: + LD DE, 0x1500 ; 21 x 0 + CALL tm_screen + LD DE, 0x7f20 ; 126 x ' ' + CALL tm_rpt_print + LD DE, 0x1500 ; 21 x 0 + JP tm_screen + +; ------------------------------------------------------ +; Print the character the specified number of times +; Inp: E - character +; D - count +; ------------------------------------------------------ +tm_rpt_print: + LD C, E ; DE=363D +rpt_next: + CALL tm_sym_out + DEC D + RET Z + JP rpt_next + +; Turn on duplicate output to printer +tm_ltab: + LD A,10h + LD (TM_VARS.tm_ltb), A + JP tm_mon + +tm_lps: + LD A, (TM_VARS.tm_ltb) + CP 0x10 + RET NZ + LD (TM_VARS.tm_ltb+1), A + LD DE,esc_cmd_kod ;= 1Bh + JP tm_lprint + +tm_lpr: + XOR A + LD (TM_VARS.tm_ltb), A + LD (TM_VARS.tm_ltb+1), A + RET + +tm_lprn: + EX DE, HL + CALL tm_lprint + JP tm_mon + +; ------------------------------------------------------ +; Print string until SUB symbol +; Inp: DE -> string ended with SUB (0x1A) +; ------------------------------------------------------ +tm_lprint: + LD A, (DE) + LD C, A + CP ASCII_SUB + RET Z + CALL tm_char_print + INC DE + JP tm_lprint + +; ------------------------------------------------------ +; Send character to printer +; Inp: C - character +; ------------------------------------------------------ +tm_char_print: + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP Z, tm_char_print ; Wait printer ready +cp_wait_ack: + IN A, (KBD_DD78PB) + AND PRINTER_ACK + JP NZ, cp_wait_ack + LD A, C + CPL + OUT (LPT_DD67PA), A + LD A, 00001001b ; set Printer Strobe (port C 4th bit) + OUT (DD67CTR), A + LD A, 00001000b ; reset Printer Strobe + OUT (DD67CTR), A + RET + +; ------------------------------------------------------ +; Wait and read data from UART +; Out: A - 7 bit data +; ------------------------------------------------------ +m_serial_in: + IN A, (UART_DD72RR) + AND RX_READY + JP Z, m_serial_in ; wait for rx data ready + IN A, (UART_DD72RD) + AND 0x7f ; leave 7 bits + RET + +; ------------------------------------------------------ +; Send data by UART +; Inp: C - data to transmitt +; ------------------------------------------------------ +m_serial_out: + IN A, (UART_DD72RR) + AND TX_READY + JP Z, m_serial_out ; Wait for TX ready + LD A, C + OUT (UART_DD72RD), A + RET +tm_plus: + LD HL, (TM_VARS.tm_hrg) + INC H + JP tm_ff +tm_minus: + LD HL, (TM_VARS.tm_hrg) + DEC H + JP tm_ff +tm_f1: + LD H, 0x1 + JP tm_ff +tm_f2: + LD H, 0x40 + JP tm_ff +tm_f3: + LD H, 0xb0 + JP tm_ff +tm_ff: + XOR A + LD L, A + LD (TM_VARS.tm_hrg), HL + JP tm_mon +tm_f4: + LD A,H + ADD A, 0x10 + JP tm_fa +tm_f5: + LD A,H + SUB 0x10 +tm_fa: + LD H, A + JP tm_ff + +tm_msg_search: + db "\fSEARCH [", 0 + +tm_search: + LD (TM_VARS.tm_hrg), HL +tm_search0: + LD DE, tm_msg_search ;= "\fSEARCH [" + CALL tm_print + LD HL, TM_VARS.tm_stack +tm_s0: + LD B, 0x1 +tm_s1: + CALL tm_get_key_ctrl_c + CP ASCII_BS + JP Z, tm_search0 + CP ASCII_CR + JP Z, tm_do_search + CP '*' + JP Z, tm_s4 + LD A, (TM_VARS.tm_tbu) + CP ASCII_TAB + JP NZ, tm_s2 +tm_s4: + LD (HL), C + CALL tm_sym_out + JP tm_s5 +tm_s2: + LD A, C + CALL tm_poke + LD A, B + OR A + JP NZ, tm_s1 +tm_s5: + INC L + JP tm_s0 + +tm_do_search: + LD B,L + LD D,H + XOR A + LD E, A + LD HL, (TM_VARS.tm_hrg) + INC HL + ADD A,B + JP Z, tm_sea1 + LD (TM_VARS.tm_stack+16), A +tm_sea1: + LD A, (DE) + CP (HL) + JP NZ, tm_s3 +tm_s6: + DEC B + JP Z, tm_aga + INC E +tm_sea2: + INC L + JP NZ, tm_sea1 + INC H + JP NZ, tm_sea1 + JP tm_main +tm_s3: + LD A, (DE) + CP '*' + JP Z, tm_s6 + LD A, (TM_VARS.tm_stack+16) + LD B, A + LD E, 0x0 + JP tm_sea2 + +tm_aga: + LD A, (TM_VARS.tm_stack+16) + +tm_ogo: + DEC A + JP Z, tm_rets + DEC HL + JP tm_ogo + +tm_rets: + LD (TM_VARS.tm_hrg), HL + JP tm_main + +m_print_log_sep: + LD C, ASCII_US + CALL tm_sym_out + JP warm_boot + +m_msg_contacts: + db " (Chernogolovka Mosk.reg. Tel 51-24)", 0 + +LAST EQU $ +CODE_SIZE EQU LAST-0xE000 +FILL_SIZE EQU 8192-CODE_SIZE + + DISPLAY "Code size is: ",/A,CODE_SIZE + +FILLER + DS FILL_SIZE, 0xFF + DISPLAY "Filler size is: ",/A,FILL_SIZE + + ENDMODULE + + OUTEND + + INCLUDE "tm_vars.inc" diff --git a/MON_v8_3edeb015/.gitignore b/MON_v8_3edeb015/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/MON_v8_3edeb015/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/MON_v8_3edeb015/BIN/MON_V8_3edeb015.BIN b/MON_v8_3edeb015/BIN/MON_V8_3edeb015.BIN new file mode 100644 index 0000000..4a573b1 Binary files /dev/null and b/MON_v8_3edeb015/BIN/MON_V8_3edeb015.BIN differ diff --git a/MON_v8_3edeb015/README.md b/MON_v8_3edeb015/README.md new file mode 100644 index 0000000..924270f --- /dev/null +++ b/MON_v8_3edeb015/README.md @@ -0,0 +1,17 @@ +# Ocean-240.2 ROM Monitor V8 checksum 3edeb015 + +Source codes of Monitor v8 for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +## Differences: + +1) Font. Russian letters б and д; +2) Calculate values for extended ram access in procedures m_ramdisk_read, m_ramdisk_write. + +## Compile: + +Code is located in memory at address: 0xE000..0xFFFF + + sjasmplus --sld=monitor.sld --sym=monitor.labels --raw=monitor.obj --fullpath monitor.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/MON_v8_3edeb015/bios_entries.inc b/MON_v8_3edeb015/bios_entries.inc new file mode 100644 index 0000000..117a2c8 --- /dev/null +++ b/MON_v8_3edeb015/bios_entries.inc @@ -0,0 +1,41 @@ + +; ======================================================= +; Ocean-240.2 +; Definition of CPM BIOS entries to compile depended +; modules +; +; By Romych 2025-09-09 +; ====================================================== + IFNDEF _BIOS + DEFINE _BIOS + + MODULE BIOS + +boot_f EQU 0xD600 +wboot_f EQU 0xD603 +const_f EQU 0xD606 +conin_f EQU 0xD609 +conout_f EQU 0xD60C +list_f EQU 0xD60F +punch_f EQU 0xD612 +reader_f EQU 0xD615 +home_f EQU 0xD618 +seldsk_f EQU 0xD61B +settrk_f EQU 0xD61E +setsec_f EQU 0xD621 +setdma_f EQU 0xD624 +read_f EQU 0xD627 +write_f EQU 0xD62A +sectran_f EQU 0xD630 +reserved_f1 EQU 0xD633 +reserved_f2 EQU 0xD636 +tape_read_f EQU 0xD639 +tape_write_f EQU 0xD63C +tape_wait_f EQU 0xD63F + +bios_var04 EQU 0xD64B + + ENDMODULE + + + ENDIF diff --git a/MON_v8_3edeb015/equates.inc b/MON_v8_3edeb015/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/MON_v8_3edeb015/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/MON_v8_3edeb015/font-6x7.inc b/MON_v8_3edeb015/font-6x7.inc new file mode 100644 index 0000000..221a101 --- /dev/null +++ b/MON_v8_3edeb015/font-6x7.inc @@ -0,0 +1,165 @@ + ; 96 symbols 6x7, with codes 0x20..0x7f KOI-7 H0 +m_font_cp0: + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; ' ' - 0x20 + DB 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04 ; '!' - 0x21 + DB 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00 ; '"' - 0x22 + DB 0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a ; '#' - 0x23 + DB 0x04, 0x1e, 0x05, 0x0e, 0x14, 0x0f, 0x04 ; '$' - 0x24 + DB 0x03, 0x13, 0x08, 0x04, 0x02, 0x19, 0x18 ; '%' - 0x25 + DB 0x06, 0x09, 0x05, 0x02, 0x15, 0x09, 0x16 ; '&' - 0x26 + DB 0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 ; ' - 0x27 + DB 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08 ; '(' - 0x28 + DB 0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02 ; ')' - 0x29 + DB 0x00, 0x0a, 0x04, 0x1f, 0x04, 0x0a, 0x00 ; '*' - 0x2a + DB 0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00 ; '+' - 0x2b + DB 0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x02 ; ',' - 0x2c + DB 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00 ; '-' - 0x2d + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06 ; '.' - 0x2e + DB 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00 ; '/' - 0x2f + DB 0x0e, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0e ; '0' - 0x30 + DB 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x0e ; '1' - 0x31 + DB 0x0e, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1f ; '2' - 0x32 + DB 0x1f, 0x08, 0x04, 0x08, 0x10, 0x11, 0x0e ; '3' - 0x33 + DB 0x08, 0x0c, 0x0a, 0x09, 0x1f, 0x08, 0x08 ; '4' - 0x34 + DB 0x1f, 0x01, 0x0f, 0x10, 0x10, 0x11, 0x0e ; '5' - 0x35 + DB 0x0c, 0x02, 0x01, 0x0f, 0x11, 0x11, 0x0e ; '6' - 0x36 + DB 0x1f, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02 ; '7' - 0x37 + DB 0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e ; '8' - 0x38 + DB 0x0e, 0x11, 0x11, 0x1e, 0x10, 0x08, 0x06 ; '9' - 0x39 + DB 0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00 ; ':' - 0x3a + DB 0x00, 0x06, 0x06, 0x00, 0x06, 0x04, 0x02 ; ';' - 0x3b + DB 0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08 ; '<' - 0x3c + DB 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00 ; '=' - 0x3d + DB 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02 ; '>' - 0x3e + DB 0x0e, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04 ; '?' - 0x3f + DB 0x0e, 0x11, 0x10, 0x16, 0x15, 0x15, 0x0e ; '@' - 0x40 + DB 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11 ; 'A' - 0x41 + DB 0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f ; 'B' - 0x42 + DB 0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e ; 'C' - 0x43 + DB 0x07, 0x09, 0x11, 0x11, 0x11, 0x09, 0x07 ; 'D' - 0x44 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f ; 'E' - 0x45 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x01 ; 'F' - 0x46 + DB 0x0e, 0x11, 0x01, 0x1d, 0x11, 0x11, 0x1e ; 'G' - 0x47 + DB 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 ; 'H' - 0x48 + DB 0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e ; 'I' - 0x49 + DB 0x1c, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06 ; 'J' - 0x4a + DB 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11 ; 'K' - 0x4b + DB 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f ; 'L' - 0x4c + DB 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 ; 'M' - 0x4d + DB 0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11 ; 'N' - 0x4e + DB 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; 'O' - 0x4f + DB 0x0f, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x01 ; 'P' - 0x50 + DB 0x0e, 0x11, 0x11, 0x11, 0x15, 0x09, 0x16 ; 'Q' - 0x51 + DB 0x0f, 0x11, 0x11, 0x0f, 0x05, 0x09, 0x11 ; 'R' - 0x52 + DB 0x1e, 0x01, 0x01, 0x0e, 0x10, 0x10, 0x0f ; 'S' - 0x53 + DB 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 'T' - 0x54 + DB 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; 'U' - 0x55 + DB 0x11, 0x11, 0x11, 0x11, 0x0a, 0x0a, 0x04 ; 'V' - 0x56 + DB 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0a ; 'W' - 0x57 + DB 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 ; 'X' - 0x58 + DB 0x11, 0x11, 0x11, 0x0a, 0x04, 0x04, 0x04 ; 'Y' - 0x59 + DB 0x1f, 0x10, 0x08, 0x04, 0x02, 0x01, 0x1f ; 'Z' - 0x5a + DB 0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e ; '[' - 0x5b + DB 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00 ; '\' - 0x5c + DB 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e ; ']' - 0x5d + DB 0x0e, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00 ; '^' - 0x5r + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f ; '_' - 0x5f + DB 0x1c, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00 ; '`' - 0x60 + DB 0x00, 0x00, 0x0e, 0x10, 0x1e, 0x13, 0x1e ; 'a' - 0x61 + DB 0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x0f ; 'b' - 0x62 + DB 0x00, 0x00, 0x0e, 0x01, 0x01, 0x01, 0x0e ; 'c' - 0x63 + DB 0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1e ; 'd' - 0x64 + DB 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x0e ; 'e' - 0x65 + DB 0x18, 0x04, 0x04, 0x0e, 0x04, 0x04, 0x04 ; 'f' - 0x66 + DB 0x00, 0x0e, 0x11, 0x11, 0x1e, 0x10, 0x0e ; 'g' - 0x67 + DB 0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11 ; 'h' - 0x68 + DB 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04 ; 'i' - 0x69 + DB 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x06 ; 'j' - 0x6a + DB 0x01, 0x01, 0x09, 0x05, 0x03, 0x05, 0x09 ; 'k' - 0x6b + DB 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08 ; 'l' - 0x6c + DB 0x00, 0x00, 0x0f, 0x15, 0x15, 0x15, 0x15 ; 'm' - 0x6d + DB 0x00, 0x00, 0x09, 0x13, 0x11, 0x11, 0x11 ; 'n' - 0x6e + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e ; 'o' - 0x6f + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x0f, 0x01 ; 'p' - 0x70 + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x1e, 0x10 ; 'q' - 0x71 + DB 0x00, 0x00, 0x0d, 0x13, 0x01, 0x01, 0x01 ; 'r' - 0x72 + DB 0x00, 0x00, 0x1e, 0x01, 0x0e, 0x10, 0x0f ; 's' - 0x73 + DB 0x04, 0x04, 0x0e, 0x04, 0x04, 0x04, 0x18 ; 't' - 0x74 + DB 0x00, 0x00, 0x11, 0x11, 0x11, 0x19, 0x16 ; 'u' - 0x75 + DB 0x00, 0x00, 0x11, 0x11, 0x0a, 0x0a, 0x04 ; 'v' - 0x76 + DB 0x00, 0x00, 0x11, 0x15, 0x15, 0x15, 0x0a ; 'w' - 0x77 + DB 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11 ; 'x' - 0x78 + DB 0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0c ; 'y' - 0x79 + DB 0x00, 0x00, 0x1f, 0x08, 0x04, 0x02, 0x1f ; 'z' - 0x7a + DB 0x0c, 0x02, 0x02, 0x01, 0x02, 0x02, 0x0c ; '{' - 0x7b + DB 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; '|' - 0x7c + DB 0x03, 0x04, 0x04, 0x08, 0x04, 0x04, 0x03 ; '}' - 0x7d + DB 0x00, 0x02, 0x15, 0x0a, 0x15, 0x08, 0x00 ; '~' - 0x7e + DB 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15 ; [DEL] - 0x7f + +; 64 symbols 6x7, with codes 0x40..0x7f KOI-7 H1 +m_font_cp1: + DB 0x00, 0x00, 0x09, 0x15, 0x17, 0x15, 0x09 ; ю - 0x40 + DB 0x00, 0x00, 0x06, 0x08, 0x0e, 0x09, 0x16 ; а - 0x41 + DB 0x07, 0x02, 0x04, 0x0e, 0x09, 0x09, 0x06 ; б - 0x42 DIFF + DB 0x00, 0x00, 0x09, 0x09, 0x09, 0x1f, 0x10 ; ц - 0x43 + DB 0x03, 0x04, 0x08, 0x0e, 0x09, 0x09, 0x06 ; д - 0x44 DIFF + DB 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x1e ; е - 0x45 + DB 0x00, 0x04, 0x0e, 0x15, 0x15, 0x0e, 0x04 ; ф - 0x46 + DB 0x00, 0x00, 0x0f, 0x09, 0x01, 0x01, 0x01 ; г - 0x47 + DB 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11 ; х - 0x48 + DB 0x00, 0x00, 0x11, 0x19, 0x15, 0x13, 0x11 ; и - 0x49 + DB 0x0a, 0x04, 0x11, 0x19, 0x15, 0x13, 0x11 ; й - 0x4a + DB 0x00, 0x00, 0x11, 0x09, 0x07, 0x09, 0x11 ; к - 0x4b + DB 0x00, 0x00, 0x1c, 0x12, 0x12, 0x12, 0x11 ; л - 0x4c + DB 0x00, 0x00, 0x11, 0x1b, 0x15, 0x11, 0x11 ; м - 0x4d + DB 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11 ; н - 0x4e + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e ; о - 0x4f + DB 0x00, 0x00, 0x1f, 0x11, 0x11, 0x11, 0x11 ; п - 0x50 + DB 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x14, 0x12 ; я - 0x51 + DB 0x00, 0x00, 0x07, 0x09, 0x07, 0x01, 0x01 ; р - 0x52 + DB 0x00, 0x00, 0x0e, 0x01, 0x01, 0x01, 0x0e ; с - 0x53 + DB 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04 ; т - 0x54 + DB 0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0e ; у - 0x55 + DB 0x00, 0x00, 0x15, 0x15, 0x0e, 0x15, 0x15 ; ж - 0x56 + DB 0x00, 0x00, 0x03, 0x05, 0x07, 0x09, 0x07 ; в - 0x57 + DB 0x00, 0x00, 0x01, 0x01, 0x07, 0x09, 0x07 ; ь - 0x58 + DB 0x00, 0x00, 0x11, 0x11, 0x13, 0x15, 0x13 ; ы - 0x59 + DB 0x00, 0x00, 0x0e, 0x11, 0x0c, 0x11, 0x0e ; з - 0x5a + DB 0x00, 0x00, 0x15, 0x15, 0x15, 0x15, 0x1f ; ш - 0x5b + DB 0x00, 0x00, 0x07, 0x08, 0x0e, 0x08, 0x07 ; э - 0x5c + DB 0x00, 0x00, 0x15, 0x15, 0x15, 0x1f, 0x10 ; щ - 0x5d + DB 0x00, 0x00, 0x09, 0x09, 0x0e, 0x08, 0x08 ; ч - 0x5e + DB 0x00, 0x00, 0x06, 0x05, 0x0c, 0x14, 0x0c ; ъ - 0x5f + DB 0x09, 0x15, 0x15, 0x17, 0x15, 0x15, 0x09 ; Ю - 0x60 + DB 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11 ; А - 0x61 + DB 0x1f, 0x11, 0x01, 0x0f, 0x11, 0x11, 0x1f ; Б - 0x62 + DB 0x09, 0x09, 0x09, 0x09, 0x09, 0x1f, 0x10 ; С - 0x63 + DB 0x0c, 0x0a, 0x0a, 0x0a, 0x0a, 0x1f, 0x11 ; Д - 0x64 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f ; Е - 0x65 + DB 0x04, 0x0e, 0x15, 0x15, 0x15, 0x0e, 0x04 ; Ф - 0x66 + DB 0x1f, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01 ; Г - 0x67 + DB 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 ; Х - 0x68 + DB 0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11 ; И - 0x69 + DB 0x04, 0x15, 0x11, 0x19, 0x15, 0x13, 0x11 ; Й - 0x6a + DB 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11 ; К - 0x6b + DB 0x1c, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11 ; Л - 0x6c + DB 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 ; М - 0x6d + DB 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 ; Н - 0x6e + DB 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; О - 0x6f + DB 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 ; П - 0x70 + DB 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x12, 0x11 ; Я - 0x71 + DB 0x0f, 0x11, 0x11, 0x11, 0x0f, 0x01, 0x01 ; Р - 0x72 + DB 0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e ; С - 0x73 + DB 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; Т - 0x74 + DB 0x11, 0x11, 0x11, 0x11, 0x1e, 0x10, 0x0e ; У - 0x75 + DB 0x15, 0x15, 0x15, 0x0e, 0x15, 0x15, 0x15 ; Ж - 0x76 + DB 0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f ; В - 0x77 + DB 0x01, 0x01, 0x01, 0x0f, 0x11, 0x11, 0x0f ; Ь - 0x78 + DB 0x11, 0x11, 0x11, 0x13, 0x15, 0x15, 0x13 ; Ы - 0x79 + DB 0x0e, 0x11, 0x10, 0x0c, 0x10, 0x11, 0x0e ; З - 0x7a + DB 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f ; Ш - 0x7b + DB 0x0e, 0x11, 0x10, 0x1c, 0x10, 0x11, 0x0e ; Э - 0x7c + DB 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f, 0x10 ; Щ - 0x7d + DB 0x11, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10 ; Ч - 0x7e + DB 0x1f, 0x15, 0x1f, 0x15, 0x1f, 0x15, 0x1f ; [DEL] - 0x7f diff --git a/MON_v8_3edeb015/io.inc b/MON_v8_3edeb015/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/MON_v8_3edeb015/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/MON_v8_3edeb015/m_vars.inc b/MON_v8_3edeb015/m_vars.inc new file mode 100644 index 0000000..578bddd --- /dev/null +++ b/MON_v8_3edeb015/m_vars.inc @@ -0,0 +1,101 @@ +; ======================================================= +; Ocean-240.2 +; Module M_VARS - Monitor variables +; RAM Range: 0xBA09-0xBFFF +; +; Disassembled by Romych 2025-02-05 +; ======================================================= + + IFNDEF _M_VARS + DEFINE _M_VARS + + MODULE M_VARS + ORG 0xbf00 + +buffer DS 128 ; 0xbf00 search text or buffer to search? + DS 36 +paint_stack EQU $ ; 0xbfa4 + DS 32 +stack1: EQU $ ; 0xbfc4 + DS 1 +paint_var1 DS 1 ; 0xbfc5 +paint_var2 DS 1 ; 0xbfc6 +paint_var3 DS 1 ; 0xbfc7 +paint_var4 DS 1 ; 0xbfc8 +paint_var5 DS 1 ; 0xbfc9 +paint_y DS 1 ; 0xbfca +paint_var7 DS 1 ; 0xbfcb +cmp_color DS 1 ; 0xbfcc +paint_sp_save DS 2 + +; Right pixel mask ex: 11111000 +pixel_mask_r DS 1 ; 0xbfcf +tmp_color DS 1 ; 0xbfd0 +rect_var2 DS 1 ; 0xbfd1 +stack_0 EQU $ +rect_var3 DS 1 ; 0xbfd2 +esc_mode DS 1 ; 0xbfd3 +esc_cmd DS 1 ; 0xbfd4 + +esc_param_cnt DS 1 ; 0xbfd5 +esc_param DS 7 ; 0xbfd6 + +; Left inverse pixel mask ex: 00011111 +pixel_mask_l_i DS 1 +; Right inverse pixel mask ex: 00011111 +pixel_mask_r_i DS 1 +; Left pixel mask ex: 11100000 +pixel_mask_l DS 1 + +; Current screen mode, bit 3 - hide/show cursor, bit 4 - only 20 rows +screen_mode DS 1 ; 0xbfe0 +cursor_row DS 1 ; 0xbfe1 Cursor Y position +cursor_col DS 1 ; 0xbfe2 Cursor X position +curr_color DS 2 ; 0xbfe3 Current color low and hi bytes +esc_hex_cmd: +row_shift DS 1 ; 0xbfe5 +codepage DS 1 ; 0xbfe6 + + +cur_palette DS 1 ; 0xbfe7 00bbbfff - background and foreground colors +beep_period DS 2 ; 0xbfe8 +beep_duration DS 2 ; 0xbfea +pix_shift DS 1 ; 0xbfec +strobe_state DS 1 ; 0xbfed +ul_var0 DS 1 ; 0xbfee +ul_A_var1 DS 1 ; 0xbfef +ul_B_var2 DS 1 ; 0xbff0 +ul_var3 DS 1 ; 0xbff1 +ul_A_var4 DS 1 ; 0xbff2 +ul_B_var5 DS 1 ; 0xbff3 +ul_var6 DS 1 ; 0xbff4 +esc_var0 DS 1 ; 0xbff5 +esc_var1 DS 1 ; 0xbff6 +esc_var2 DS 1 ; 0xbff7 +esc_var3 DS 1 ; 0xbff8 + DS 1 ; 0xbff9 + DS 1 ; 0xbffa + DS 1 ; 0xbffb + DS 1 ; 0xbffc + DS 1 ; 0xbffd + DS 1 ; 0xbffe + DS 1 ; 0xbfff + + ASSERT stack1 = 0xbfc4 + ASSERT buffer = 0xbf00 + ASSERT paint_var1 = 0xbfc5 + ASSERT pixel_mask_r = 0xbfcf + ASSERT stack_0 = 0xbfd2 + ASSERT esc_mode = 0xbfd3 + ASSERT screen_mode = 0xbfe0 + ASSERT cur_palette = 0xbfe7 + ASSERT ul_var0 = 0xbfee + ASSERT paint_stack = 0xbfa4 + ASSERT esc_var3 = 0xbff8 + + ;DISPLAY "screen_mode: ", /H, screen_mode + ;DISPLAY "fn48_var1: ", /H, fn48_var1 + + ENDMODULE + + ENDIF diff --git a/MON_v8_3edeb015/monitor.asm b/MON_v8_3edeb015/monitor.asm new file mode 100644 index 0000000..262dbb5 --- /dev/null +++ b/MON_v8_3edeb015/monitor.asm @@ -0,0 +1,5594 @@ +; ====================================================== +; Ocean-240.2 +; Monitor V8 checksum 3edeb015 +; +; Disassembled by Romych 2026-02-15 +; ====================================================== + + DEVICE NOSLOT64K + + INCLUDE "io.inc" + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "bios_entries.inc" + + OUTPUT monitor_E000.bin + + + MODULE MONITOR + + ORG 0xE000 + +; ------------------------------------------------------ +; Monitor Entry points +; ------------------------------------------------------ + +start: JP m_hot_start ; E000 +mon_hexb: JP m_hexb ; E003 +non_con_status: JP m_con_status ; E006 +mon_con_in: JP m_con_in ; E009 +mon_con_out: JP m_con_out ; E00C +mon_serial_in: JP m_serial_in ; E00F +mon_serial_out: JP m_serial_out ; E012 +mon_char_print: JP m_char_print ; E015 +mon_tape_read: JP m_tape_read ; E018 +mon_tape_write: JP m_tape_write ; E01B +ram_disk_read: JP m_ramdisk_read ; E01E +ram_disk_write: JP m_ramdisk_write ; E021 +mon_tape_read_ram: JP m_tape_read_ram2 ; E024 +mon_tape_write_ram: JP m_tape_write_ram2 ; E027 +mon_tape_wait: JP m_tape_wait ; E02A +mon_tape_detect: JP m_tape_blk_detect ; E02D +read_floppy: JP m_read_floppy ; E030 +write_floppy: JP m_write_floppy ; E033 +mon_out_str_z: JP m_out_strz ; E036 + JP m_fn_39 ; E039 + JP get_image_hdr ; E03C + JP esc_picture ; E03F + JP m_print_at_xy ; E042 C-char esc_param[0]=X, [1]=Y + JP esc_draw_fill_rect ; E045 + JP esc_paint ; E048 + JP esc_draw_line ; E04B + JP esc_draw_circle ; E04E + + +; ------------------------------------------------------ +; Init system devices +; ------------------------------------------------------ +m_hot_start: + DI + ;LD SP, M_VARS.rst_ret_jp + LD A, 10000000b ; DD17 all ports to out + OUT (SYS_DD17CTR), A ; VV55 Sys CTR + OUT (DD67CTR), A ; VV55 Video CTR + + ; init_kbd_tape + LD A, 0x93 + OUT (KBD_DD78CTR), A + + + LD A, 01111111b ; VSU=0, C/M=1, FL=111, COL=111 + OUT (VID_DD67PB), A ; color mode + LD A, 00000001b + OUT (SYS_DD17PB), A ; Access to VRAM + LD B, 0x0 ; TODO: replace to LD HL, 0x3f00 LD B,L + LD HL, 0x3f00 + LD A, H + ADD A, 0x41 ; A=128 0x80 + + ; Clear memory from 0x3F00 to 0x7FFF +.fill_video: + LD (HL), B + INC HL + CP H + JP NZ, .fill_video + + ;XOR A + LD A, 0 + OUT (SYS_DD17PB), A ; Disable VRAM + LD A, 00000111b + OUT (SYS_DD17PC), A ; pix shift to 7 + LD (M_VARS.pix_shift), A + + XOR A + LD (M_VARS.screen_mode), A + LD (M_VARS.row_shift), A + + ; Set color mode and palette + LD (M_VARS.curr_color+1), A + CPL + LD (M_VARS.curr_color), A + LD A, 00000011b + LD (M_VARS.cur_palette), A + ; VSU=0, C/M=1, FL=000, COL=011 + ; color mode, black border + ; 00-black, 01-red, 10-purple, 11-white + LD A, 01000011b + OUT (VID_DD67PB), A + + ; config LPT + LD A, 0x4 + OUT (DD67PC), A ; bell=1, strobe=0 + LD (M_VARS.strobe_state), A ; store strobe + LD HL, 1024 ; 683us + LD (M_VARS.beep_period), HL + LD HL, 320 ; 213us + LD (M_VARS.beep_duration), HL + +.conf_uart: + ; Config UART + LD A, 11001110b + OUT (UART_DD72RR), A + LD A, 00100101b + OUT (UART_DD72RR), A + + ; Config Timer#1 for UART clock + LD A, 01110110b ; tmr#1, load l+m bin, sq wave + OUT (TMR_DD70CTR), A + + ; 1.5M/20 = 75kHz + LD A, 20 + OUT (TMR_DD70C2), A + XOR A + OUT (TMR_DD70C2), A +.conf_pic: + ; Config PIC + LD A,00010010b ; ICW1 edge trigger, interval 8, sin... + OUT (PIC_DD75RS), A + XOR A + OUT (PIC_DD75RM), A ; ICW2 + CPL + OUT (PIC_DD75RM), A ; ICW3 no slave + LD A,00100000b + OUT (PIC_DD75RS), A ; Non-specific EOI command, End of I... + LD A, PIC_POLL_MODE + OUT (PIC_DD75RS), A ; Poll mode, poll on next RD + + LD A, 0x80 + OUT (KBD_DD78PC), A ; TODO: - Check using this 7th bit + NOP + NOP + XOR A + OUT (KBD_DD78PC), A + + ; Init cursor + LD SP, M_VARS.stack1 + CALL m_draw_cursor + + ; Beep + LD C, ASCII_BELL + CALL m_con_out + + LD A, (BIOS.boot_f) + CP JP_OPCODE + JP Z, BIOS.boot_f + LD HL, mgs_system_nf + CALL m_out_strz + JP m_sys_halt + +; -------------------------------------------------- +; Output ASCIIZ string +; Inp: HL -> string +; -------------------------------------------------- +m_out_strz: + LD C, (HL) + LD A, C + OR A + RET Z + CALL m_con_out + INC HL + JP m_out_strz + +mgs_system_nf: + DB "\r\nSYSTEM NOT FOUND\r\n", 0 + +m_sys_halt: + HALT + +; ------------------------------------------------------ +; Console status +; Out: A = 0 - not ready +; A = 0xFF - ready (key pressed) +; ------------------------------------------------------ +m_con_status: + IN A, (PIC_DD75RS) ; Read PIC status + NOP + AND KBD_IRQ ; Check keyboard request RST1 + LD A, 0 + RET Z ; no key pressed + CPL + RET ; key pressed + +; ------------------------------------------------------ +; Wait and read data from UART +; Out: A - 7 bit data +; ------------------------------------------------------ +m_serial_in: + IN A, (UART_DD72RR) + AND RX_READY + JP Z, m_serial_in ; wait for rx data ready + IN A, (UART_DD72RD) + AND 0x7f ; leave 7 bits + RET + +; ------------------------------------------------------ +; Read key +; Out: A +; ------------------------------------------------------ +m_con_in: + CALL m_con_status + OR A + JP Z, m_con_in ; wait key + IN A, (KBD_DD78PA) ; get key + AND 0x7f ; reset hi bit, leave 0..127 code + PUSH AF + ; TODO: Check if it is keyboard ACK + ; PC7 Set Hi (ACK?) + LD A, 0x80 + OUT (KBD_DD78PC), A + ; PC7 Set Lo + XOR A + OUT (KBD_DD78PC), A + POP AF + RET + +; ------------------------------------------------------ +; Send data by UART +; Inp: C - data to transmitt +; ------------------------------------------------------ +m_serial_out: + IN A, (UART_DD72RR) + AND TX_READY + JP Z, m_serial_out ; Wait for TX ready + LD A, C + OUT (UART_DD72RD), A + RET + +; ------------------------------------------------------ +; Send character to printer +; Inp: C - character +; ------------------------------------------------------ +m_char_print: + ; wait printer ready + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP Z, m_char_print + + LD A, C + NOP + OUT (LPT_DD67PA), A + ; set LP strobe + LD A, 00010100b + OUT (DD67PC),A + +.wait_lp: + ; wait printer ack + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP NZ, .wait_lp + ; remove LP strobe + LD A, 00000100b + OUT (DD67PC), A + RET + +; ------------------------------------------------------ +; Out char to console +; Inp: C - char +; ------------------------------------------------------ +m_con_out: + PUSH HL + PUSH DE + PUSH BC + CALL m_con_out_int + POP BC + POP DE + POP HL + RET + +; ------------------------------------------------------ +; Out char C to console +; ------------------------------------------------------ +m_con_out_int: + LD DE, M_VARS.esc_mode + LD A, (DE) + DEC A + OR A ; TODO: unused (save 1b 4t) + JP M, m_print_no_esc ; esc_mode=0 - standart print no ESC mode + JP NZ, m_print_at_xy ; esc_mode=2 (graphics) + + ; handle ESC param (esc_mode=1) + INC DE ; TODO: replace to INC E E=0xd3 save 2t + LD A, (DE) + OR A + JP P, get_esc_param + LD A, C + AND 0xf ; convert char to command code + LD (DE), A + INC DE ; TODO: replace to INC E E=0xd3 save 2t + XOR A + LD (DE), A + RET + +get_esc_param: + LD HL, M_VARS.esc_cmd + LD B, (HL) ; TODO: replace to INC L L=0xd4 save 2t + INC HL ; HL -> param count + LD A, (HL) + INC A + LD (HL), A + ; store new param + LD E, A + LD D, 0x0 + ADD HL, DE ; HL -> parameter[param_count] + LD (HL), C ; store letter as esc parameter + ; get params count for esc command + LD HL, esc_params_tab + LD E, B ; d=0, b = cmd + ADD HL, DE ; DE - command offset + CP (HL) + ; return if enough + RET M + +;esc_set_mode: + LD HL, M_VARS.esc_cmd + LD A, (HL) + AND 0x0f ; mask (cmd=0..15) + LD E, A + DEC HL ; HL -> esc_mode + OR A + LD (HL), 2 ; mode=2 for cmd=0 + RET Z ; just return, no handler there + + LD D, 0 ; TODO: remove, D already 0 + LD (HL), D ; reset mode to 0 for other + DEC DE ; DE = cmd-1 + +;co_get_hdlr: + ; Calc ESC command handler offset + LD HL, esc_handler_tab + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) + ; HL = addr of handler func + EX DE, HL + ; It is 1..4 func DRAW_* func? + CP 0x4 + JP P, esc_no_draw_fn + LD A, (M_VARS.screen_mode) + AND 00000011b + ; If not in graphics mode - exit + JP NZ, esc_exit + +esc_no_draw_fn: + LD DE, esc_exit + PUSH DE + + ; Jump to ESC func handler + JP (HL) + +esc_exit: + XOR A + LD (M_VARS.esc_mode), A + RET + + ; Count of parameters for ESC commands + ; 0xe1cb +esc_params_tab: + DB 3, 5, 4, 3, 1, 2, 1, 1 + DB 1, 2, 1, 5, 5, 7, 6, 4 + +esc_handler_tab: + DW esc_draw_fill_rect ;5 1x1y1x2y2m + DW esc_draw_line ;4 2x1y1x2y2 + DW esc_draw_dot ;3 3xxyy + DW esc_set_color ;1 4N N=1..4 + DW esc_set_cursor ;2 5rc r-Row, c-Col + DW esc_set_vmode ;1 6m m-mode: + ; C 0 - 40x25 cursor on + ; M 1,2 - 64x25 cursor on + ; M 3 - 80x25 cursor on + ; C 4 - 40x25 cursor off + ; M 5,6 - 64x25 cursor off + ; M 7 - 80x25 cursor off + ; M 8 - 20rows mode + ; 9 - cursor off + ; 10 - cursor on + DW esc_set_charset ;1 7n where n is: + ; 0 - LAT Both cases + ; 1 - RUS Both cases + ; 2 - LAT+RUS Upper case + DW esc_set_palette ;1 8c c - Foreground+Backgound + DW esc_set_cursor2 ;2 9xy + DW esc_print_screen ;1 : + DW esc_draw_circle ;5 ;xyraxay X,Y, Radius, aspect ratio X, aspect ratio Y + DW esc_paint ;5 = + DW esc_picture ;6 > + DW esc_set_beep ;4 ?ppdd pp-period (word), dd - duration (word) + +esc_set_beep: + ; param byte 1+2 -> period + LD DE, M_VARS.esc_param + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (M_VARS.beep_period), HL + ; param byte 3+4 -> duration + INC DE + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (M_VARS.beep_duration), HL + RET + +esc_set_cursor2: + JP esc_set_cursor + +esc_print_screen: + LD A, (M_VARS.screen_mode) + AND 00000011b + RET NZ ; ret if not 0-3 mode + LD DE, 0x30ff + CALL m_print_hor_line + DEC E + LD D, 0xf0 + +.chk_keys: + CALL m_con_status + OR A + JP Z, .no_keys + CALL m_con_in + CP ASCII_ESC + RET Z + +.no_keys: + CALL m_print_hor_line + DEC E + JP NZ, .chk_keys + LD D, 0xe0 ; 224d + CALL m_print_hor_line + RET + +; ------------------------------------------------------ +; Print line to printer +; D - width +; ------------------------------------------------------ +m_print_hor_line: + LD HL, cmd_esc_set_X0 + + ; Set printer X coordinate = 0 + CALL m_print_cmd + LD HL, 4 + LD (M_VARS.ul_var0), HL ; Set start coord X = 4 + LD B, 0x0 ; TODO: LD B, H (save 1b 3t) + +.print_next_col: + LD C, 0x0 + ; 1 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (M_VARS.ul_var0) + INC HL + + ; inc X + LD (M_VARS.ul_var0), HL + LD C, 0x1 + ; 2 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (M_VARS.ul_var0) + INC HL + ; inc X + LD (M_VARS.ul_var0), HL + INC B + LD A, B + CP 236 + JP C, .print_next_col + LD HL, cmd_esc_inc_Y2 + CALL m_print_cmd + RET + +; ------------------------------------------------------ +; Send command to printer +; Inp: HL -> command bytes array +; ------------------------------------------------------ +m_print_cmd: + PUSH BC +.print_nxt: + LD A, (HL) + CP ESC_CMD_END + JP Z, .cmd_end + LD C, A + CALL m_char_print + INC HL + JP .print_nxt +.cmd_end: + POP BC + RET + +; ------------------------------------------------------ +; Print 7 vertical pixels to printer +; Inp: A - value to print +; ------------------------------------------------------ +m_print_vert_7pix: + PUSH AF + ; Set coordinate X to 0 + LD HL, cmd_esc_set_X + CALL m_print_cmd + LD HL, (M_VARS.ul_var0) + LD C,H + CALL m_char_print + LD C,L + CALL m_char_print + ; Set column print mode + LD HL, cmd_esc_print_col + CALL m_print_cmd + POP AF + ; Print 7 vertical pixels + LD C, A + CALL m_char_print + RET + +; ------------------------------------------------------ +; Control codes for printer УВВПЧ-30-004 +; ------------------------------------------------------ +; Zn - Increment Y coordinate +; 0xe2a5 +cmd_esc_inc_Y2: + DB ASCII_ESC + DB 'Z' + DB 2h + DB ESC_CMD_END + +; Xnn - Set X coordinate +cmd_esc_set_X0: + DB ASCII_ESC + DB 'X' + DB 0h ; 0..479 + DB 0h + DB ESC_CMD_END + +; ------------------------------------------------------ +; X - Start on "Set X coordinate" command +; ------------------------------------------------------ +cmd_esc_set_X: + DB ASCII_ESC + DB 'X' + DB ESC_CMD_END + +; O - Column print (vertical 7 bit) +cmd_esc_print_col: + DB ASCII_ESC + DB 'O' + DB ESC_CMD_END + +; ------------------------------------------------------ +; Get 7 vertical pixels from screen +; Inp: C - sheet +; Out: A - byte +; ------------------------------------------------------ +m_get_7vpix: + LD A, (M_VARS.row_shift) + ADD A, B + ADD A, 19 ; skip first 20pix + LD L, A + PUSH DE + PUSH BC + LD A, E + +.calc_pix_no: + AND 0x7 + LD B, A + LD A, E + ; calc hi addr + RRA ; /8 + RRA + RRA + AND 0x1f + ADD A, A ; *2 + ADD A, 64 ; bytes per row + LD H, A + ; select sheet 0|1 + LD A, C + AND 0x1 + ADD A, H + LD H, A + ; HL = pix addr, turn on VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD E, (HL) ; read pixel + INC H ; HL += 512 + INC H + LD D, (HL) ; read pixel row+1 + + ; turn off VRAM access + ;v8 XOR A + LD A, 0 + OUT (SYS_DD17PB), A +.for_all_pix: + DEC B + JP M, .all_shifted + ; shift pixels D >> [CF] >> E + LD A, D + RRA + LD D, A + LD A, E + RRA + LD E, A + JP .for_all_pix +.all_shifted: + LD A, E + LD D, 0 + RRA + JP NC,.not_1_1 + LD D,00110000b +.not_1_1: + RRA + JP NC, .not_1_2 + LD A, D + OR 11000000b + LD D, A +.not_1_2: + LD A, D + POP BC + POP DE + RET + +esc_set_palette: + LD A, (M_VARS.esc_param) + AND 00111111b ; bgcol[2,1,0],pal[2,1,0] + LD (M_VARS.cur_palette), A + LD B, A + LD A, (M_VARS.screen_mode) + AND 00000011b + LD A, 0x0 + JP NZ, esp_no_colr + LD A, 0x40 + +esp_no_colr: + OR B + OUT (VID_DD67PB), A + RET + +esc_set_charset: + LD A, (M_VARS.esc_param) + AND 0x3 ; charset 0..3 + LD (M_VARS.codepage), A + RET + +; ------------------------------------------------------ +; Get address for draw symbol glyph +; Inp: A - ascii code +; Out: HL -> glyph offset +; ------------------------------------------------------ +m_get_glyph: + LD L, A ; L = ascii code + LD E, A ; E = ascii code + XOR A + LD D, A + LD H, A + ; HL = DE = ascii code + ADD HL, HL + ADD HL, DE + ADD HL, HL + ADD HL, DE + ; HL = A * 7 + LD A, E ; A = A at proc entry + CP '@' + ; First 64 symbols is same for all codepages + JP M, .cp_common + LD A, (M_VARS.codepage) + OR A + ; cp=0 - Latin letters + JP Z, .cp_common + DEC A + ; cp=1 - Russian letters + JP Z, .cp_rus + ; cp=2 - 0x40..0x5F - displayed as Lat + ; 0x60 - 0x7F - displayed as Rus + LD A, E + CP 0x60 + JP M, .cp_common +.cp_rus: + LD DE, 448 ; +448=64*7 Offset for cp1 + ADD HL, DE + +.cp_common: + LD DE, m_font_cp0-224 ; m_font_cp0-32*7 + ADD HL, DE ; add symbol glyph offset + RET + + +; -------------------------------------------------- +; Console output +; Inp: C - char +; -------------------------------------------------- +m_print_no_esc: + LD A, C + AND 0x7f ; C = 0..127 ASCII code + CP ASCII_SP ; C < ' '? + JP M, m_handle_esc_code ; jump if less + CALL m_get_glyph + EX DE, HL + LD A, (M_VARS.screen_mode) + AND 0x3 + JP NZ, mp_mode_64 ; jump to non color modes + + CALL calc_addr_40 + INC L + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + DEC H + DEC H + ; one or two bytes + LD A, B + OR B + JP Z, .l1 + DEC B + JP Z, .l2 + DEC B + JP Z, .l3 + JP .l4 +.l1: + INC H + INC H + LD BC, 0xffc0 + LD A, 0x0 + JP .l5 +.l2: + LD BC, 0xf03f + LD A, 0x6 + JP .l5 +.l3: + LD BC, 0xfc0f + LD A, 0x4 + JP .l5 +.l4: + LD BC, 0xff03 + LD A, 0x2 +.l5: + LD (M_VARS.esc_var1), A + EX DE, HL + +.sym_draw: + LD A, (M_VARS.esc_var1) + PUSH HL + LD L, (HL) + LD H, 0x0 + OR A + JP Z, .pne_l8 + +.pne_l7: + ADD HL, HL + DEC A + JP NZ, .pne_l7 + +.pne_l8: + EX DE, HL + LD A, (HL) + AND C + LD (HL), A + LD A, (M_VARS.curr_color) + AND E + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND C + LD (HL), A + LD A, (M_VARS.curr_color+1) + AND E + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, (M_VARS.curr_color) + AND D + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, (M_VARS.curr_color+1) + AND D + OR (HL) + LD (HL), A + INC L + DEC H + DEC H + DEC H + EX DE, HL + POP HL + INC HL + LD A, (M_VARS.esc_var0) + DEC A + LD (M_VARS.esc_var0), A + JP NZ, .sym_draw + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + ; draw cursor on return + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_rt: + INC HL + LD A, (HL) ; a = col + ADD A, 1 ; col+1 + AND 0x3f ; screen column 0..63 + LD (HL), A ; save new col + CP 40 + DEC HL + RET M ; Return if no wrap + +m40_wrap_rt: + INC HL + XOR A + LD (HL), A + DEC HL + LD A, (M_VARS.screen_mode) + AND 0x08 ; screen_mode=8? + JP NZ, m2_lf + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_bksp: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A + AND 0x3f ; A=0..63 + CP 0x3f + JP Z, .wrap + LD (HL), A + DEC HL + RET + +.wrap: + LD A, 39 + LD (HL), A + DEC HL + ; and cursor up + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_up: + LD A, (HL) + SUB 10 ; 10 rows per symbol + JP NC, .up_no_minus + LD A, 240 ; wrap to bottom +.up_no_minus: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor right 8 pos) 20rows mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m20_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x3f ; wrap A=0..63 + LD (HL), A + CP 40 + DEC HL + RET M ; ret if column <40 + JP m40_wrap_rt ; or wrap to next line + +; -------------------------------------------------- +; Calculate VRAM address in 40 column mode +; -------------------------------------------------- +calc_addr_40: + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + CP 4 + LD B, A + JP M, .l2 + AND 0x3 + LD B, A + LD A, H + OR A + RRA + OR A + RRA + LD C, A + LD H, 0x6 + XOR A + +.l1: + ADD A, H + DEC C + JP NZ, .l1 + ADD A, B + +.l2: + ADD A, B + ADD A, 66 + LD H, A + LD A, 0x7 + LD (M_VARS.esc_var0),A + RET + +m2_lf: + LD A, (HL) + ADD A, 10 + CP 15 + JP NC, .lf_nowr + LD (HL), A + RET + +.lf_nowr: + LD A, (M_VARS.row_shift) + LD L, A + ADD A, ASCII_LF + LD E, A + LD C, ASCII_BS + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.cas_l5: + LD B, 0x40 + LD H, 0x40 ; TODO: LD H, B save 1b 3t + LD D, H + +.cas_l6: + LD A, (DE) + LD (HL), A + INC H + INC D + DEC B + JP NZ, .cas_l6 + INC L + INC E + DEC C + JP NZ, .cas_l5 + LD C, 10 + LD A, (M_VARS.row_shift) + ADD A, 8 + LD E, A + +.cas_l7: + LD B, 0x40 + LD D, 0x40 ; TODO: LD D, B save 1b 3t + XOR A + +.cas_l8: + LD (DE),A + INC D + DEC B + JP NZ,.cas_l8 + INC E + DEC C + JP NZ,.cas_l7 + LD A,0x0 + OUT (SYS_DD17PB),A + RET + + +; --------------------------------------------------- +; Handle ASCII_BS (cursor left) in 20row mode +; --------------------------------------------------- +m20_bksp: + INC HL + LD A, (HL) + OR A + DEC HL + RET Z + + INC HL + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x3f + LD (HL), A + DEC HL + RET + +; --------------------------------------------------- +; Print symbol in 64x25 mode +; --------------------------------------------------- +mp_mode_64: + CP 3 ; + JP Z, mp_mode_80 ; jump for screen_mode=3 + ; calc symbol address in VRAM + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + ADD A, 0x40 + LD H, A + ; + LD C, 7 ; symbol height + + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + EX DE, HL + XOR A + LD (DE), A + INC E + +.next_row: + LD A, (HL) + ADD A, A + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .next_row + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + ; draw cursor at end + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_rt: + INC HL + LD A, (HL) + ADD A, 1 + AND 0x3f ; wrap + LD (HL), A + DEC HL + RET NZ ; ret if no wrap + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Scroll Up for 10 rows +; -------------------------------------------------- +scroll_up: + LD A, (M_VARS.row_shift) + ADD A, 10 + OUT (SYS_DD17PA), A ; Scroll via VShift register + LD (M_VARS.row_shift), A ; store new VShift value + ; calc bottom 16 rows address in VRAM + LD HL, 0x40f0 ; 240th VRAM byte + ADD A, L + LD L, A + LD C, H + + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + XOR A + LD DE, 0x1040 ; D=16 E=64 (512/8 bytes in row) + +.next_row: + LD H, C + LD B, E + + ; clear 64 bytes (512px in mono or 256px in color mode) +.next_col: + LD (HL), A + INC H ; next column + DEC B + JP NZ, .next_col + INC L ; next row address + DEC D ; row counter - 1 + JP NZ, .next_row + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_bs: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x3f ; wrap column (0..63) + LD (HL), A + CP 63 + DEC HL + RET NZ + ; cursor up if wrapped + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_up: + LD A, (HL) + SUB 10 + JP NC, .no_wrap + LD A, 240 + +.no_wrap: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor column + 8) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x38 + LD (HL), A + DEC HL + RET NZ ; return if no wrap + ; cursor down if wrap + JP m64_lf + +; -------------------------------------------------- +; Print symbols in 80x25 mode +; -------------------------------------------------- +mp_mode_80: + CALL calc_addr_80 + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + ; fix address + EX DE, HL + INC E + ; make bitmask + LD A, B + OR A + JP Z, .l1 + DEC A + JP Z, .l2 + DEC A + JP Z, .l3 + JP .l4 + +.l1: + LD B, (HL) + LD A, (DE) + AND 0xc0 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .l1 + JP .l6 +.l2: + LD A, (HL) + RRCA + RRCA + AND 0x7 + LD B, A + LD A, (DE) + AND 0xf0 + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + AND 0xc0 + LD B, A + DEC D + LD A, (DE) + AND 0x1f + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, .l2 + JP .l6 +.l3: + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0x1 + LD B, A + LD A, (DE) + AND 0xfc + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0xf0 + LD B, A + DEC D + LD A, (DE) + AND 0x7 + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, .l3 + JP .l6 +.l4: + DEC D +.l5: + LD A, (HL) + RLCA + RLCA + LD B, A + LD A, (DE) + AND 0x1 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .l5 + INC D + +.l6: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + ; Draw cursor after symbol + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_rt: + INC HL + LD A, (HL) + ADD A, 1 ; inc column + AND 0x7f + LD (HL), A + CP 80 + DEC HL + RET M ; return if no wrap + +m80_col_wrap: + INC HL + XOR A + LD (HL), A + DEC HL + ; and move cursor to next row + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_bs: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x7f ; mask [0..127] + CP 127 + JP Z, .wrap + LD (HL), A + DEC HL + RET + +.wrap: + LD A, 79 + LD (HL), A + DEC HL + ; and move cursor to previous line + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_up: + LD A, (HL) + SUB 10 + JP NC, .no_wrap + LD A, 240 + +.no_wrap: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor column + 8) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x7f + LD (HL), A + CP 80 + DEC HL + RET M ; return if no cursor wrap + JP m80_col_wrap + +; -------------------------------------------------- +; Calculate address for cursor pos for 80x25 mode +; Out: HL -> VRAM +; B -> pixel pos in byte +; -------------------------------------------------- +calc_addr_80: + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + CP 4 + LD B, A + JP M, mns_ep_fm_0 + AND 3 + LD B, A + LD A, H + OR A + RRA + OR A + RRA + LD C, A + LD H, 3 + XOR A +mns_l1: + ADD A, H + DEC C + JP NZ, mns_l1 + ADD A, B +mns_ep_fm_0: + ADD A, 0x42 + LD H, A + LD C, 0x7 + RET + +; -------------------------------------------------- +; Clear screen and set cursor to 0,0 +; Inp: HL -> cursor position +; -------------------------------------------------- +m_clear_screen: + LD A, (M_VARS.screen_mode) + AND 0x8 + JP NZ, m_clear_20_rows ; for bit 4 is set, clear only 20 rows + ; all in black + LD A, 01111111b + OUT (VID_DD67PB), A ; C/M=1 FL=111 CL=111 All black + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD DE, video_ram + EX DE, HL + LD A, H + ADD A, 0x40 ; A=0x80 + LD B, 0 + +.fill_scrn: + LD (HL), B + INC HL + CP H + JP NZ, .fill_scrn ; fill while HL<0x8000 + + EX DE, HL + LD A, (M_VARS.cur_palette) + LD B, A ; B = current palette + LD A, (M_VARS.screen_mode) + AND 0x3 ; color? + LD A, 0x0 + JP NZ, .mono_mode + LD A, 01000000b +.mono_mode: + OR B + ; Restore mode and palette + OUT (VID_DD67PB), A + + ; And set cursor to home position + +; -------------------------------------------------- +; Set cursor to 0,0 and close VRAM access +; Inp: HL -> cursor_row +; -------------------------------------------------- +m_cursor_home: + XOR A + NOP + NOP + LD (HL), A + INC HL + XOR A + LD (HL), A + DEC HL + ;XOR A + LD A, 0 + ; Disable VRAM access + OUT (SYS_DD17PB), A + RET + +; Clear only 20 rows +m_clear_20_rows: + ; take row shift in account + LD A, (M_VARS.row_shift) + LD L, A + LD C, 20 + + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.next_row: + LD H, 0x40 ; HL = 0x4000 + shift_row + LD B, 64 ; 64 bytes at row + XOR A +.next_col: + LD (HL), A + INC H ; next column + DEC B + JP NZ, .next_col + INC L ; next row + DEC C + JP NZ, .next_row + ; Disabe VRAM access + LD A, 0 + OUT (SYS_DD17PB), A + JP m_cursor_home + +; -------------------------------------------------- +; Draw cursor at current cursor position +; if not hidden +; -------------------------------------------------- +m_draw_cursor: + LD A, (M_VARS.screen_mode) + AND 0x4 ; check hidden cursor bit + RET NZ ; return if hidden + LD A, (M_VARS.screen_mode) + AND 0x3 ; check color mode (40 column mode 6x7 font) + JP NZ, .dc_mode_64 + + EX DE, HL + LD HL, (M_VARS.cursor_row) + LD A, H ; cursor column + CP 40 ; > 40? + EX DE, HL + RET P ; ret if column out of screen + + PUSH HL + EX DE, HL + CALL calc_addr_40 + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; previous address + DEC H + DEC H + INC L + LD C, 7 ; cursor size + ; build masks + LD A, B + OR B + JP Z, .dc_rt2 + DEC B + JP Z, .dc_mid + DEC B + JP Z, .dc_lt + JP .dc_rt1 +.dc_rt2: + INC H + INC H + LD DE, 0x001f + JP .dc_put +.dc_mid: + LD DE, 0x07c0 + JP .dc_put +.dc_lt: + LD DE, 0x01f0 + JP .dc_put +.dc_rt1: + LD DE, 0x007c + +.dc_put: + ; xor cursor mask with VRAM[HL] value + ; left bytes + LD A, (HL) + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR E + LD (HL), A + ; right bytes + INC H + LD A, (HL) + XOR D + LD (HL), A + INC H + LD A, (HL) + XOR D + LD (HL), A + ; next cursor row address + INC L + DEC H + DEC H + DEC H + DEC C + JP NZ, .dc_put ; draw next cursor row if c>0 + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + RET + + ; draw cursor in 64 column mode +.dc_mode_64: + CP 3 ; screen_mode = 3 - 80 rows + JP Z, .dc_mode_80 + EX DE, HL + LD HL, (M_VARS.cursor_row) ; H - col, L - row + ; take into account the vertical shift + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + ; + LD A, H + CP 64 ; check column + EX DE, HL + RET P ; return if column out of screen + EX DE, HL + ; calc VRAM address + ADD A, 0x40 + LD H, A + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD BC, 0x7f08 ; B=01111111b - mask, C=8 - cursor size +.cur_64_next: + ; xor with VRAM content + LD A, (HL) + XOR B + LD (HL), A + ; next row address + INC L + DEC C + JP NZ, .cur_64_next + EX DE, HL + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + + ; draw cursor in 80 column mode +.dc_mode_80 + EX DE, HL + LD HL, (M_VARS.cursor_row) + + LD A, H + CP 80 + EX DE, HL + RET P ; return if column > 80 + + PUSH HL + CALL calc_addr_80 + LD C, 7 ; cursor size + INC L + + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; mask + LD A, B + OR A + LD B, 0x1f + JP Z, .dc_1_byte + DEC A + LD DE, 0xc007 + JP Z, .dc_2_byte + DEC A + LD DE, 0xf001 + JP Z, .dc_2_byte + LD B, 0x7c + DEC H + JP .dc_1_byte ; TODO: unused + +.dc_1_byte: + ; xor with VRAM byte + LD A, (HL) + XOR B + LD (HL), A + INC L + DEC C + JP NZ, .dc_1_byte + JP .dc_80_end + +.dc_2_byte: + ; xor with previous byte + DEC H + LD A, (HL) + XOR D + LD (HL), A + ; xor with current byte + INC H + LD A, (HL) + XOR E + LD (HL), A + ; next cursor address + INC L + DEC C + JP NZ, .dc_2_byte + +.dc_80_end: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + RET + +; -------------------------------------------------- +; If ESC character, turn esc_mode ON +; Inp: A - ASCII symbol +; -------------------------------------------------- +m_handle_esc_code: + CP ASCII_ESC + JP NZ, m_handle_control_code + ; turn on ESC mode for next chars + LD HL, M_VARS.esc_mode + LD (HL), 0x1 ; turn on ESC mode + INC HL + LD (HL), 0xff ; esc_cmd = 0xff + RET + +; -------------------------------------------------- +; Handle one byte ASCII control code +; Inp: A - ASCII symbol +; -------------------------------------------------- +m_handle_control_code: + CP ASCII_BELL + JP Z, m_beep + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + PUSH AF + CALL m_draw_cursor + LD A, (M_VARS.screen_mode) + AND 0x08 ; 20-rows mode? + JP Z, handle_cc_common ; jump for normal screen modes + + ; for hidden cursor modes + POP AF + CP ASCII_TAB ; TAB + JP Z, m20_tab + CP ASCII_BS ; BKSP + JP Z, m20_bksp + CP ASCII_CAN ; Cancel + JP Z, m40_rt + CP ASCII_US ; ASCII Unit separator + JP Z, m_clear_20_rows + CP ASCII_LF ; LF + JP Z, m2_lf + CP ASCII_CR ; CR + RET NZ ; ret on unknown + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; Handle cursor for 40x25, 64x25, 80x25 modes +; -------------------------------------------------- +handle_cc_common: + POP AF + CP ASCII_US + JP Z, m_clear_screen + CP ASCII_FF + JP Z, m_cursor_home + PUSH AF + LD A, (M_VARS.screen_mode) + AND 3 ; check for color modes + JP NZ, .handle_cc_mono + ; 32x25 text mode + POP AF + CP ASCII_TAB ; cursor right +8 + JP Z, m20_tab + CP ASCII_BS ; cursor left + JP Z, m40_bksp + CP ASCII_CAN ; cursor right + JP Z, m40_rt + CP ASCII_EM ; cursor up + JP Z, m40_up + CP ASCII_SUB + JP Z, m40_lf ; cursor down + CP ASCII_LF + JP Z, m40_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 ; move cursor to first column for CR + DEC HL + RET + +; -------------------------------------------------- +; Handle control chars for 64x25 or 80x25 modes +; -------------------------------------------------- +.handle_cc_mono: + LD A, (M_VARS.screen_mode) + CP 3 + JP Z, handle_cc_80x25 + CP 7 + JP Z, handle_cc_80x25 + ; 64x25 screen mode + POP AF + CP ASCII_TAB + JP Z, m64_tab + CP ASCII_BS + JP Z, m64_bs + CP ASCII_CAN + JP Z, m64_rt + CP ASCII_EM + JP Z, m64_up + CP ASCII_SUB + JP Z, m64_lf + CP ASCII_LF + JP Z, m64_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; Handle control chars for 80x25 modes +; -------------------------------------------------- +handle_cc_80x25: + POP AF + CP ASCII_TAB + JP Z, m80_tab + CP ASCII_BS + JP Z, m80_bs + CP ASCII_CAN + JP Z, m80_rt + CP ASCII_EM + JP Z, m80_up + CP ASCII_SUB + JP Z, m80_lf + CP ASCII_LF + JP Z, m80_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; +; -------------------------------------------------- +m_beep: + LD HL, (M_VARS.beep_duration) + EX DE, HL + LD HL, (M_VARS.beep_period) + LD A, 00110110b ; TMR#0 LSB+MSB Square Wave Generator + OUT (TMR_DD70CTR), A + LD A, L ; LSB + OUT (TMR_DD70C1), A + LD A, H ; MSB + OUT (TMR_DD70C1), A + LD A, (M_VARS.strobe_state) + LD B, A +m_bell_cont: + LD A, D ; DE=duration + OR E + RET Z ; ret if enough + DEC DE + LD A, B + XOR BELL_PIN + LD B, A + OUT (DD67PC), A ; Invert bell pin +m_bell_wait_tmr1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; 0x10 + JP NZ, m_bell_wait_tmr1 + LD A, B + XOR BELL_PIN ; Flip pin again + LD B, A + OUT (DD67PC), A +m_bell_wait_tmr2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP Z,m_bell_wait_tmr2 + JP m_bell_cont + + +; ------------------------------------------------------ +; 5 Set cursor position +; ------------------------------------------------------ +esc_set_cursor: + CALL m_draw_cursor + LD DE, M_VARS.esc_param + LD HL, M_VARS.cursor_col + INC DE + LD A, (DE) ; column + SUB 32 + LD B, A + LD A, (M_VARS.screen_mode) + CP 3 + JP Z, .mode_80 + CP 7 + JP Z, .mode_80 + OR A + JP Z, .mode_40 + CP 4 + JP Z, .mode_40 + ; mode 64x25 + LD A, B + CP 64 + JP M, .common + LD A, 64 + JP .common + ; mode 40x25 +.mode_40: + LD A, B + CP 40 + JP M, .common + LD A, 40 + JP .common + ; mode 80x25 +.mode_80: + LD A, B + CP 80 + JP M, .common + LD A, 80 +.common: + LD (HL), A + DEC DE + DEC HL + LD A, (DE) + SUB 32 + CP 24 + JP C, esc_le_24 + LD A, 24 +esc_le_24: + LD B, A + ADD A, A + ADD A, A + ADD A, B + ADD A, A + LD (HL), A + CALL m_draw_cursor ; TODO change call+ret to jp + RET ; + +; ------------------------------------------------------ +; 6n Set video mode or cursor visibility +; Inp: n is +; 0 - C 32x25 with cursor; 0000 +; 1 - M 64x25 with cursor; 0001 +; 2 - M 64x25 with cursor; 0010 +; 3 - M 80x25 with cursor; 0011 +; 4 - C 32x25 no cursor; 0100 +; 5 - M 64x25 no cursor; 0101 +; 6 - M 64x25 no cursor; 0110 +; 7 - M 80x25 no cursor; 0111 +; 8 - M 20rows mode 1000 +; 9 - hide cursor 1001 +; 10 - show cursor 1010 +; ------------------------------------------------------ +esc_set_vmode: + LD HL, M_VARS.screen_mode + LD A, (M_VARS.cur_palette) + LD B, A + LD A, (M_VARS.esc_param) ; first parameter - video mode + AND 0xf + CP 11 + RET NC ; return if not valid input parameter + CP 9 + JP Z, .cursor_hide + CP 10 + JP Z, .cursor_show + LD (HL), A ; store new mode + CP 4 + JP Z, .set_color_mode + AND 0x3 ; monochrome (80x25, 64x25) mode? + LD A, 0 ; mode 512x254 mono + JP NZ, .skip_for_mono_mode + ; mode=0 or 4 -> 256x256px color +.set_color_mode: + LD A, 0x40 ; color mode +.skip_for_mono_mode: + OR B ; color mode with palette + OUT (VID_DD67PB), A ; configure screen mode + + LD HL, M_VARS.cursor_row + CALL m_clear_screen + +.draw_cursor: + CALL m_draw_cursor ; TODO change call+ret to jp + RET + +.cursor_hide: + LD A, (HL) ; screen_mode + OR 00000100b ; cursor hide + LD (HL), A + LD HL, M_VARS.cursor_row + JP .draw_cursor + +.cursor_show: + LD A, (HL) ; screen_mode + AND 11111011b ; cursor show + LD (HL), A + JP .draw_cursor + + +; ------------------------------------------------------ +; 4n n=1..4 Set drawing color +; ------------------------------------------------------ +esc_set_color: + LD A, (M_VARS.esc_param) +m_set_color: + AND 0x3 + RRA + LD B, A + LD A, 0x0 ; TODO: unused + SBC A, A + LD (M_VARS.curr_color), A + LD A, B + DEC A + CPL + LD (M_VARS.curr_color+1), A + RET + +;--------------------------------------------------- +; Print symbol or print sprite at X,Y coordinates +; Inp: param x,y +; C - character or sprite_no to draw +;--------------------------------------------------- +m_print_at_xy: + ; check video mode + LD A, (M_VARS.screen_mode) + AND 0x3 ; color? + JP NZ, esc_exit ; exit for mono modes + + LD A, C + AND 0x7f + LD C, A ; C = C with 7th bit reset + CP 0x1 + JP Z, .sprites_en ; enable sprite mode + + CP ASCII_SP + JP M, mode2_exit ; codes 0..31 - turm off game_mode + + ; check X, Y range to prevent drawing symbols out of screen + LD HL, M_VARS.esc_param + LD A, (HL) + LD E, A + ADD A, 8 + JP C, mode2_exit ; exit if esc_param[0]>247 + LD (HL), A + INC HL ; HL -> esc_param[1] + LD A, 247 + CP (HL) + JP C, mode2_exit ; exit if esc_param[1]>247 + ; calculate X,Y pixel address in VRAN + LD D, (HL) + CALL calc_px_addr + ; HL - address, B - pixel pos in byte + LD A, L + SUB 8 + LD L, A + PUSH HL ; save address + + LD A, (M_VARS.esc_var2) + OR A + JP NZ, .mode_sp + + ; font + LD A, C + CALL m_get_glyph + LD C, 7 + POP DE + INC E + JP .out_sp + + ; sprite mode +.mode_sp: + LD A, C + SUB 32 + CP 35 + JP NC, co_ex_l08 + + ; Calc sprite address + LD L, A ; HL=A - sprite_no + XOR A + LD H, A + ADD HL, HL + ADD HL, HL + ADD HL, HL ; HL=HL*8 + LD DE, game_sprite_tab + ADD HL, DE ; HL -> sprite + LD C, 8 ; bytes count + POP DE + + ; Out sprite + ; DE -> VRAM address + ; C - height +.out_sp: + LD A, (M_VARS.esc_param+2) + DEC A + JP Z, out_no_xor + +.next_line: + PUSH HL + ; Access Video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD L, (HL) ; load from table + LD H, 0x0 + LD A, B + OR A + JP Z, .l05 +.l04: + ADD HL, HL + DEC A + JP NZ, .l04 +.l05: + EX DE, HL + LD A, (M_VARS.curr_color) + AND E + XOR (HL) + LD (HL), A + INC H + INC H + LD A, (M_VARS.curr_color) + AND D + XOR (HL) + LD (HL), A + DEC H + LD A, (M_VARS.curr_color+1) + AND E + XOR (HL) + LD (HL), A + INC H + INC H + LD A, (M_VARS.curr_color+1) + AND D + XOR (HL) + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + ; Disable VRAM + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + INC HL + DEC C + JP NZ, .next_line + RET + +.sprites_en: + LD (M_VARS.esc_var2), A + RET + +mode2_exit: + XOR A + LD (M_VARS.esc_var2), A + JP esc_exit + +co_ex_l08: + POP DE + JP mode2_exit + +out_no_xor: + PUSH HL + ; Acess to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD L, (HL) + LD H, 0x0 + LD A, B + OR A + JP Z, .l11 +.l10: + ADD HL, HL + DEC A + JP NZ, .l10 +.l11: + EX DE, HL + PUSH BC + LD A, (M_VARS.curr_color) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + LD A, (M_VARS.curr_color+1) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + POP BC + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP HL + INC HL + DEC C + JP NZ, out_no_xor + RET + +game_sprite_tab: + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; 0x00 + DB 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E ; 0x01 + DB 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E ; 0x02 + DB 0x00, 0x08, 0x08, 0x14, 0x63, 0x14, 0x08, 0x08 ; 0x03 + DB 0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00 ; 0x04 + DB 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00 ; 0x05 + DB 0x1C, 0x3E, 0x1C, 0x7F, 0x7F, 0x6B, 0x08, 0x1C ; 0x06 + DB 0x08, 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x08, 0x1C ; 0x07 + DB 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18 ; 0x08 + DB 0x18, 0xDB, 0x3C, 0xE7, 0xE7, 0x3C, 0xDB, 0x18 ; 0x09 + DB 0xE7, 0xE7, 0x00, 0x7E, 0x7E, 0x00, 0xE7, 0xE7 ; 0x0a + DB 0x7E, 0x81, 0x81, 0xFF, 0xFF, 0x81, 0x81, 0x7E ; 0x0b + DB 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18 ; 0x0c + DB 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00 ; 0x0d + DB 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00 ; 0x0e + DB 0x00, 0x08, 0x0C, 0xFE, 0xFE, 0x0C, 0x08, 0x00 ; 0x0f + DB 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 ; 0x10 + DB 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 ; 0x11 + DB 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0 ; 0x12 + DB 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F ; 0x13 + DB 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ; 0x14 + DB 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF ; 0x15 + DB 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F ; 0x16 + DB 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0 ; 0x17 + DB 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 ; 0x18 + DB 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 ; 0x19 + DB 0x70, 0x08, 0x76, 0xFF, 0xFF, 0xFF, 0x7E, 0x18 ; 0x1a + DB 0xC3, 0xDB, 0xDB, 0x18, 0x18, 0xDB, 0xDB, 0xC3 ; 0x1b + DB 0xFC, 0xCC, 0xFC, 0x0C, 0x0C, 0x0E, 0x0F, 0x07 ; 0x1c + DB 0xFE, 0xC6, 0xFE, 0xC6, 0xC6, 0xE6, 0x67, 0x03 ; 0x1d + DB 0x18, 0x3C, 0x3C, 0x18, 0x7E, 0x18, 0x24, 0x66 ; 0x1e + DB 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 ; 0x1f + DB 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 ; 0x20 + DB 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 0x21 + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF ; 0x22 + +; -------------------------------------------------- +; Calculate address of pixel in Video RAM +; Inp: DE - Y, X +; Out: HL - address +; B - offset in byte +; -------------------------------------------------- +calc_px_addr: + ; take into account the vertical displacement + LD A, (M_VARS.row_shift) + SUB D + DEC A + LD L, A + + LD A, E + AND 0x07 ; X mod 8 - offset in byte + LD B, A + + LD A, E + RRA + RRA + AND 00111110b + ADD A, 0x40 ; VRAM at 0x4000 + LD H, A + RET + + +;--------------------------------------------------- +; Draw filled rectanger +; Inp: esc param X1,Y2,X2,Y2 +; -------------------------------------------------- +esc_draw_fill_rect: + LD HL, M_VARS.esc_param + LD E, (HL) ; E=X1 + INC HL + LD C, (HL) ; C=Y1 + INC HL + INC HL + LD D, (HL) ; D=Y2 + LD A, D + SUB C ; delta Y + JP NZ, .non_zero_h + INC A ; 1 as minimum +.non_zero_h: + LD C, A ; C = height + ; DE = Y2, X1 + CALL calc_px_addr + ; HL -> videomem offset, b - pixel offset + + ; build pixel mask + XOR A +.shift_mask_l: + SCF + RLA + DEC B + JP P, .shift_mask_l + RRA + LD (M_VARS.pixel_mask_l), A + CPL ; invert + LD (M_VARS.pixel_mask_l_i), A + LD A, (M_VARS.esc_param+2) ; X2 + AND 0x7 ; 0..7 + LD B, A + XOR A +.shift_mask_r: + SCF + RLA + DEC B + JP P, .shift_mask_r + LD (M_VARS.pixel_mask_r_i), A + LD B, C + ; calc end address + LD A, (M_VARS.esc_param+2) ; X2 + RRA + RRA + AND 00111110b + ADD A, 0x40 + SUB H + RRCA + LD C, A ; C - width + INC B + LD A, (M_VARS.esc_param+4) + DEC A + JP NZ, .rectangle_xor + LD A, (M_VARS.pixel_mask_r_i) + CPL + LD (M_VARS.pixel_mask_r), A + LD D, A + LD A, (M_VARS.pixel_mask_l) + LD E, A + ; draw B horisontal lines +.next_line: + PUSH DE + PUSH HL + PUSH BC + CALL draw_line_h + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, .next_line + RET + + ; draw B horisontal lines (xor) +.rectangle_xor: + LD A, (M_VARS.pixel_mask_r_i) + LD (M_VARS.pixel_mask_r), A + LD D, A + LD A, (M_VARS.pixel_mask_l_i) + LD E, A + ; Access to VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.edf_l6: + PUSH DE + PUSH HL + PUSH BC + LD A, C + OR A + JP NZ, .w_ne_0 ; jump if width != 0 + LD A, E ; merge masks E=E or D + OR D +.next_8px: + LD E, A +.w_ne_0: + LD B, E ; B - mask + EX DE, HL + LD HL, (M_VARS.curr_color) ; color + EX DE, HL + ; Set pixels - VideoRAM[HL] = color xor VideoRAM[HL] + LD A, E + AND B + XOR (HL) + LD (HL), A + ; And next byte + INC H + LD A, D + AND B + XOR (HL) + LD (HL), A + ; ---------- + INC H + LD A, C + OR A + JP Z, .complete + DEC C + ; right tail of line, use right mask +.r_mask: + LD A, (M_VARS.pixel_mask_r) + JP Z, .next_8px + ; full 8 bits without mask at middle of line +.next_full: + LD A, (HL) + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + LD (HL), A + INC H + DEC C + JP NZ, .next_full + JP .r_mask +.complete: + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, .edf_l6 + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +;--------------------------------------------------- +; Paint screen +; Inp: params X,Y,Color,repColor +;--------------------------------------------------- +esc_paint: + ; Save stack + LD HL, 0x0 + ADD HL, SP + LD (M_VARS.paint_sp_save), HL + + ; Set our own stack + LD HL, M_VARS.paint_stack ; TODO: Z80 LD SP,var i800 - LXI SP,nn + LD SP, HL + + ; save current color + LD HL, (M_VARS.curr_color) + LD (M_VARS.tmp_color), HL + + ; set color from param 3 + LD A, (M_VARS.esc_param+2) + DEC A + CALL m_set_color + + ; color to replace, from param 4 + LD A, (M_VARS.esc_param+3) + DEC A + LD (M_VARS.cmp_color), A + + ; HL - Y,X + LD A, (M_VARS.esc_param) + LD L, A + LD A, (M_VARS.esc_param+1) + LD H, A + LD (M_VARS.paint_y), A + + LD A, (M_VARS.esc_param+4) ; 0 - full fill, 1 - fast fill + DEC A + LD (M_VARS.esc_param), A + + LD A, 0x2 + LD (M_VARS.paint_var5), A ; task_no=2 + + EX DE, HL + CALL calc_px_addr + LD (M_VARS.esc_param+1), HL ; temporary ctore address of start fill point + ; make mask + LD A, 10000000b +.l1: + RLCA + DEC B + JP P, .l1 + + LD B, A + LD (M_VARS.esc_param+3), A ; store mask + + ; find left border + LD A, (M_VARS.cmp_color) + LD C, A + LD D, E ; D = X + CALL paint_find_left + ; find right border + LD HL, (M_VARS.esc_param+1) ; restore HL + LD A, (M_VARS.esc_param+3) ; restore mask + LD B, A + CALL paint_find_right + ; + LD HL, 0x0 + PUSH HL + PUSH HL + ; + LD A, (M_VARS.esc_param) ; A = fill mode + OR A + JP Z, ep_fm_0 + ; push fill task parameters + LD A, (M_VARS.paint_var5) + DEC A ; task_no-1 + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + +ep_fm_0: + ; push fill task parameters + LD A, (M_VARS.paint_var5) ; task_no + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + JP paint_task ; exec task + +ep_task_end: + LD A, (M_VARS.cmp_color) + LD C, A ; color to compare + + LD A, (M_VARS.esc_param) ; fill mode 0 - full, 1 - fast + OR A + JP NZ, ep_f_fast + + LD A, 0x2 + LD (M_VARS.paint_var7), A + LD A, (M_VARS.paint_var2) + CP 2 + JP Z, ep_l4 + JP ep_l5 ; TODO: change to one JP NZ + +ep_l4: + LD A, 1 + LD (M_VARS.paint_var5), A ; task_no? + JP ep_l8 + +ep_l5: + LD A, 2 + LD (M_VARS.paint_var5), A + JP ep_l11 + +ep_l6: + LD A, (M_VARS.paint_var7) + OR A + JP Z, paint_task + LD A, (M_VARS.paint_var2) + CP 2 + JP Z, ep_l5 ; TODO: change to one JP NZ + JP ep_l4 + +ep_f_fast: + LD A, (M_VARS.paint_var2) + LD (M_VARS.paint_var5), A + CP 1 ; TODO: DEC A - save 1b 3t + JP Z, ep_l8 ; TODO: change to one JP NZ + JP ep_l11 + +ep_l8: + LD A, (M_VARS.paint_var3) + LD D, A + LD A, (M_VARS.paint_var1) + LD E, A + LD HL, (M_VARS.esc_param+1) + LD A, (M_VARS.esc_param+3) + LD B, A + LD A, (M_VARS.paint_var4) + DEC A + JP Z, ep_l10 + LD (M_VARS.paint_y), A + INC L + CALL paint_find_next_right + JP Z, ep_l10 + LD HL, (M_VARS.esc_param+4) + LD A, (M_VARS.esc_param+6) + LD B, A + INC L + CALL paint_find_next_left + JP Z, ep_l10 + LD A, (M_VARS.esc_param) + OR A + JP NZ, ep_l9 + JP ep_l12 + +ep_l9: + LD A, (M_VARS.paint_var5) + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + JP paint_task + +ep_l10: + LD A, (M_VARS.esc_param) + OR A + JP NZ, paint_task + LD A, (M_VARS.paint_var7) + DEC A + LD (M_VARS.paint_var7), A + JP ep_l6 + +ep_l11: + LD A, (M_VARS.paint_var3) + LD D, A + LD A, (M_VARS.paint_var1) + LD E, A + LD HL, (M_VARS.esc_param+1) + LD A, (M_VARS.esc_param+3) + LD B, A + LD A, (M_VARS.paint_var4) + INC A + CP 0xff + JP Z, ep_l10 + LD (M_VARS.paint_y), A + DEC L + CALL paint_find_next_right + JP Z, ep_l10 + LD HL, (M_VARS.esc_param+4) + LD A, (M_VARS.esc_param+6) + LD B, A + DEC L + CALL paint_find_next_left + JP Z, ep_l10 + LD A, (M_VARS.esc_param) + OR A + JP NZ, ep_l9 + JP ep_l12 + +; --------------------------------------------------- +; +; --------------------------------------------------- +paint_find_next_right: + CALL get_pixel + JP NZ, .l1 + CALL paint_find_left + LD A, 0xff + OR A + RET + +.l1: + LD A, D + CP E + RET Z + INC D + LD A, B + RLCA + LD B, A + JP NC, .l2 + INC H + INC H +.l2: + CALL get_pixel + JP NZ, .l1 + LD A, 0xff + OR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +paint_find_next_left: + CALL get_pixel + JP NZ, .l1 + CALL paint_find_right + LD A, 0xff + OR A + RET +.l1: + LD A, E + CP D + RET Z + DEC E + LD A, B + RRCA + LD B, A + JP NC, .l2 + DEC H + DEC H +.l2: + CALL get_pixel + JP NZ, .l1 + LD A, 0xff + OR A + RET + +ep_l12: + LD A, D + LD (M_VARS.pixel_mask_r), A + LD A, (M_VARS.paint_var5) + LD D, A + PUSH DE + LD A, (M_VARS.pixel_mask_r) + CP E + JP NZ, ep_l13 + LD L, A + LD A, (M_VARS.paint_y) + LD H, A + PUSH HL + JP ep_l16 +ep_l13: + LD D, E + CALL paint_find_left + LD E, D + LD A, (M_VARS.paint_y) + LD D, A + PUSH DE + LD A, (M_VARS.pixel_mask_r) + LD D, A + CP E + JP Z, ep_l16 +ep_l14: + DEC E + LD A, B + RRCA + LD B, A + JP NC, ep_l15 + DEC H + DEC H +ep_l15: + CALL get_pixel + JP NZ, ep_l14 + JP ep_l12 +ep_l16: + JP ep_l10 + +; --------------------------------------------------- +; Find rightmost pixel to fill +; In/Out: E = x_right +; HL - current pixel address +; B - pixel mask +; --------------------------------------------------- +paint_find_right: + LD A, E + CP 0xff + RET Z ; return if X=right border + INC E ; x=x+1 + ; rotate pixel mask right + LD A, B + RLCA + LD B, A + JP NC, .in_byte + ; inc addr+2 (2 byte per 8 pixels) + INC H + INC H + +.in_byte: + CALL get_pixel + JP Z, paint_find_right ; find until same color + ; border found, x-1 + DEC E + ; rotate mask back 1 px + LD A, B + RRCA + LD B, A + RET NC + ; addr-2 if previous byte + DEC H + DEC H + RET + +; --------------------------------------------------- +; Find leftmost pixel to fill +; In/Out: D = x_left +; HL - current pixel address +; B - pixel mask +; --------------------------------------------------- +paint_find_left: + LD A, D + OR A + RET Z ; return if x=0 + + DEC D ; x-1 + LD A, B + RRCA ; rotate mask to right + LD B, A + JP NC, .in_byte + DEC H ; addr-2 (2 byte for 8px) + DEC H + +.in_byte: + CALL get_pixel + JP Z, paint_find_left ; repeat until same color + + INC D ; border found, x+1 + ; mask rotate right + LD A, B + RLCA + LD B, A + RET NC + ; if CF, inc address+2 + INC H + INC H + RET + +; --------------------------------------------------- +; Inp: HL - address +; B - pixel mask +; C - color to compare +; Out: A - 0,1,2 +; ZF - set if color match +; --------------------------------------------------- +get_pixel: + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; get pixel and mask + LD A, (HL) + AND B + JP NZ, .bit1_set + INC H + LD A, (HL) + DEC H + AND B + JP NZ, .bit2_set + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + CP C + RET + +.bit1_set: + INC H + LD A, (HL) + DEC H + AND B + JP NZ, .bit12_set + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 0x1 + CP C + RET + +.bit2_set: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 0x2 + CP C + RET + +.bit12_set: + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 3 + CP C + RET + +paint_task: + POP HL ; L=x0, H=Y + LD (M_VARS.paint_var3), HL + EX DE, HL + + POP HL ; L=x1, H=mode + LD A, H + OR A + JP Z, paint_exit ; jump for mode=0 + + ; calc leftmost pixel address, mask for draw horisontal line + LD (M_VARS.paint_var1), HL + CALL calc_px_addr + LD (M_VARS.esc_param+1), HL + LD C, B + LD A, 0x80 +.lmp_mask: + RLCA + DEC B + JP P, .lmp_mask + LD (M_VARS.esc_param+3), A + ; calc rightmos pixel address and mask + LD A, (M_VARS.paint_var1) + LD E, A + LD A, (M_VARS.paint_var4) + LD D, A + CALL calc_px_addr + LD (M_VARS.esc_param+4), HL + LD D, B + LD A, 0x80 +.rmp_mask: + RLCA + DEC B + JP P, .rmp_mask + LD (M_VARS.esc_param+6), A + LD A, (M_VARS.esc_param+3) ; TODO: unused code + + XOR A +.lmi_mask: + SCF + RLA + DEC C + JP P, .lmi_mask + RRA + LD E, A ; E - left inv mask + + XOR A +.rmi_mask: + SCF + RLA + DEC D + JP P, .rmi_mask + CPL + LD D, A ; D - right inv mask + + LD (M_VARS.pixel_mask_r), A + LD HL, (M_VARS.esc_param+1) ; HL -> lext pix address + LD A, (M_VARS.esc_param+5) ; right pix address (low byte) + SUB H ; delta x + RRCA ; 2 byte for 8 pix + LD C, A ; C - line width + CALL draw_line_h + JP ep_task_end + +paint_exit: + LD HL, (M_VARS.tmp_color) ; restore previous current color + LD (M_VARS.curr_color), HL + LD HL, (M_VARS.paint_sp_save) ; restore previous stack + LD SP, HL + RET + +;--------------------------------------------------- +; Draw horizontal line +; Inp: C - width +; DE - left & right pixel mask +; HL - address of first byte of line +;--------------------------------------------------- +draw_line_h: + ; Access to VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, C + OR A + JP NZ, .width_ne0 + LD A, E ; join left and right masks + OR D +.next_byte: + LD E, A +.width_ne0: + LD B, E + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Get pixels, apply colors + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A ; store first + ; Same for second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; move to next byte + INC H + LD A, C + OR A + JP Z, .complete + DEC C +.r_mask: + ; use right mask for last right byte + LD A, (M_VARS.pixel_mask_r) + JP Z, .next_byte +.full_8: + LD (HL), E + INC H + LD (HL), D + INC H + DEC C + JP NZ, .full_8 + JP .r_mask +.complete: ; TODO: duplicate close_vram_ret + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +;--------------------------------------------------- +; 2x1y1x2y2 Draw Line +;--------------------------------------------------- +esc_draw_line: + LD HL, M_VARS.esc_param + LD E, (HL) ; E=X1 + INC HL + LD D, (HL) ; D=Y1 + INC HL + LD A, (HL) + INC HL + LD H, (HL) ; H=Y2 + LD L, A ; L=X2 + CP E + JP C, .x1_le_x2 + EX DE, HL ; exchange if X1>X2 +.x1_le_x2: + LD (M_VARS.esc_param), HL ; store x1,y1 back + LD A, E + SUB L + LD L, A ; L - width + LD A, D + SUB H + LD H, A ; H - height + PUSH AF + JP NC, .pos_height + ; change sign + CPL + INC A + LD H, A +.pos_height: + EX DE, HL + LD HL, (M_VARS.esc_param) + EX DE, HL + JP Z, height0 + LD A, L + OR A + JP Z, .width0 + LD B, A + POP AF + LD A, 0x0 + ADC A, A + LD (M_VARS.esc_param+4), A + ; HL = E/B height/width + LD E, H + LD C, 16 + LD D, 0 +.next_16: + ADD HL, HL + EX DE, HL + ADD HL, HL + EX DE, HL + LD A, D + JP C, .edl_l4 + CP B + JP C, .edl_l5 +.edl_l4: + SUB B + LD D, A + INC HL +.edl_l5: + DEC C + JP NZ, .next_16 + LD DE, 0x0 + PUSH DE + ; save result at stack + PUSH HL + + LD HL, (M_VARS.esc_param) ; x1,y1 + EX DE, HL + LD C, B + CALL calc_px_addr + ; HL - address, B - offset in byte + ; make mask + LD A, 10000000b +.roll_l: + RLCA + DEC B + JP P, .roll_l + CPL + LD B, A ; b - inv mask + +.edl_l7 + POP DE + EX (SP), HL ; save HL on top of stack + LD A, H + ADD HL, DE + SUB H + CPL + INC A + EX (SP), HL + PUSH DE + PUSH BC + LD C, A + EX DE, HL + + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Access VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD A, (M_VARS.esc_param+4) ; sign of delta Y + OR A + JP NZ, .next_down +.next_up: + ; firs byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, .is_last + DEC C + ; draw up + DEC L + JP .next_up +.next_down: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; + DEC H + LD A, C + OR A + JP Z, .is_last + DEC C + ; draw down + INC L + JP .next_down +.is_last: + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP BC + LD A, B + ; <<1px + SCF + RLA + JP C, .edl_l11 + RLA + INC H + INC H +.edl_l11 + LD B, A + DEC C + JP NZ, .edl_l7 + POP HL + POP HL + RET + +; -------------------------------------------------- +; draw vertical line +; Inp: DE - YX +; L - length +; -------------------------------------------------- +.width0 + LD C, H + CALL calc_px_addr + + ; make pixel mask + LD A, 10000000b +.edl_l13: + RLCA + DEC B + JP P, .edl_l13 + CPL + LD B, A + + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + POP AF + + ; Enable VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + JP C, .next_row_down + +.next_row_up: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next Y + DEC H + LD A, C + OR A + JP Z, close_vram_ret + DEC C + ; dec row + DEC L + JP .next_row_up + +.next_row_down: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next address + DEC H + LD A, C + OR A + JP Z, close_vram_ret + DEC C + ; inc row + INC L + JP .next_row_down + +close_vram_ret + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; draw horizontal line +; Inp: DE - YX +; L - length +; -------------------------------------------------- +height0 + POP AF + LD C, L + LD A, L + OR A + JP NZ, .len_ne0 + INC C ; length 1 at least +.len_ne0: + CALL calc_px_addr + ; make pixel mask + LD A, 10000000b +.edl_l19 + RLCA + DEC B + JP P, .edl_l19 + CPL + LD B, A + + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + +.next_col: + ; set 1st byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; set 2nd byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next byte + DEC H + ; next (right) horizontal pixel + LD A, B + SCF + RLA + JP C, .edl_l21 + RLA + INC H + INC H +.edl_l21 + LD B, A + DEC C + JP NZ, .next_col + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; ESC Draw Dot +; -------------------------------------------------- +esc_draw_dot: + LD HL, (M_VARS.esc_param) + EX DE, HL + CALL calc_px_addr + LD A, 0x80 +edd_l1 + RLCA + DEC B + JP P, edd_l1 + LD B, A + LD A, (M_VARS.esc_param+2) + CP 0x3 + JP Z, edd_ep_task_end + LD A, B + CPL + LD B, A + LD A, (M_VARS.esc_param+2) + CP 0x2 + JP Z, edd_ep_fm_0 + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + AND B + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, 0x0 + OUT (SYS_DD17PB), A + RET +edd_ep_fm_0 + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + LD A, 0x0 + OUT (SYS_DD17PB), A + RET +edd_ep_task_end + CALL get_pixel + LD (M_VARS.esc_var3), A + RET + +; -------------------------------------------------- +; +; -------------------------------------------------- +esc_picture: + LD HL, (M_VARS.esc_param+3) + LD A, (HL) + CP ':' + RET NZ + INC HL + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD A, (HL) + LD (M_VARS.esc_var0), A + INC HL + LD A, (HL) + LD (M_VARS.esc_var1), A + INC HL + PUSH HL + LD C, (HL) + INC HL + LD B, (HL) + INC HL + INC HL + EX DE, HL + PUSH DE + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL pict_sub1 + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP DE + POP HL + LD (HL), C + INC HL + LD (HL), B + CALL pict_sub2 ; TODO: replace call+ret to jp; + RET + +pict_sub1: + LD A, (M_VARS.esc_param) + CP ASCII_EM + JP Z, gih_up + CP ASCII_CAN + JP Z, gih_rt + CP ASCII_SUB + JP Z, gih_ctrl_z + CP ASCII_BS + JP Z, gih_bs + CP ASCII_US + JP Z, pict_clr + RET + +pict_clr: + LD L, C + LD H, B + LD A, (M_VARS.esc_var0) + LD C, A + LD A, (M_VARS.esc_var1) + LD B, A + CALL put_image + POP HL + POP HL + POP HL + RET + +ehd_l1: + CP 0x2 + JP Z, get_image_hdr + CP 0x3 + JP Z, m_fn_39 + RET + +; -------------------------------------------------- +; Function 39 +; -------------------------------------------------- +m_fn_39: + LD HL, (M_VARS.esc_param+2) ; pr 3,4 + INC L + LD C, L + LD B, H + LD E, L + CALL dc_mul_e_h + ADD HL, HL + EX DE, HL + LD HL, (M_VARS.esc_param) ; par 1,2 + PUSH BC + PUSH DE + LD BC, 10 + LD E, L + LD D, H + ADD HL, BC + EX DE, HL + LD (HL), E + INC HL + LD (HL), D + INC HL + POP BC + LD A, 4 +.l1: + PUSH AF ; TODO: remove AF not changed + EX DE, HL + ADD HL, BC + EX DE, HL + LD (HL), E + INC HL + LD (HL), D + INC HL + POP AF ; TODO: remove AF not changed + DEC A + JP NZ, .l1 + EX DE, HL + LD HL, 0x0 + ADD HL, BC ; HL=BC + ADD HL, BC ; HL=2*BC + ADD HL, HL ; HL=4*BC + ADD HL, BC ; HL=5*BC + ADD HL, HL ; HL=10*BC + EX DE, HL ; DE=10*BC + LD A, 0x0 + LD B, A ; B=A=0 + ; fill DE bytes at [HL] with 0 +.l2: + LD (HL), B + INC HL + DEC DE + CP E + JP NZ, .l2 + CP D + JP NZ, .l2 + + XOR A + LD (M_VARS.esc_var0), A + POP BC + LD HL, (M_VARS.esc_param) + LD DE, 10 + ADD HL, DE +.l3: + EX DE, HL + LD HL, (M_VARS.esc_param+4) + EX DE, HL + PUSH BC + CALL fn39_sub2 + POP BC + LD A, (M_VARS.esc_var0) + ADD A, 0x2 + LD (M_VARS.esc_var0), A + CP 10 + RET Z + JP .l3 + +; --------------------------------------------------- +; Function 3C +; --------------------------------------------------- +get_image_hdr: + LD HL, (M_VARS.esc_param+4) + LD (HL), 58 ; ':' + INC HL + PUSH HL + LD HL, (M_VARS.esc_param+2) + INC L + LD C, L + LD A, H + ADD A, 0x4 + LD B, A + LD H, B + LD E, C + CALL dc_mul_e_h + ADD HL, HL + EX DE, HL + POP HL + LD (HL), E + INC HL + LD (HL), D + INC HL + LD (HL), C + INC HL + LD (HL), B + INC HL + EX DE, HL + LD HL, (M_VARS.esc_param) + ADD HL, HL + EX DE, HL + PUSH BC + PUSH HL + CALL calc_px_addr + POP DE + LD A, L + LD (DE), A + INC DE + LD A, H + LD (DE), A + INC DE + LD A, B + LD (DE), A + INC DE + POP BC + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL get_image ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; = +; --------------------------------------------------- +esc_get_put_image: + LD A, (M_VARS.esc_param+6) + CP 0x2 + JP NC, ehd_l1 + LD HL, M_VARS.esc_param + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD C, (HL) + INC HL ; TODO: next call to calc_px_addr + LD B, (HL) ; destroy value of B and HL + CALL calc_px_addr + EX DE, HL + LD HL, (M_VARS.esc_param+4) + LD A, H + CP 128 + RET C + + CP 184 + RET NC + + EX DE, HL + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (M_VARS.esc_param+6) + OR A + JP NZ, put_image + +; --------------------------------------------------- +; Get image from VRAM to user buffer +; Inp: HL -> VRAM[x,y] +; DE -> buffer +; BC - width, height +; --------------------------------------------------- +get_image: + PUSH HL + PUSH BC +.next_row: + ; byte 1 + LD A, (HL) + LD (DE), A + INC H ; next Y (row) + INC DE + ; byte 2 ; next dst addr + LD A, (HL) + LD (DE), A + INC H + LD A, H + CP 128 ; last row? + JP NZ, .l2 + LD H, 0x40 ; reset Y +.l2: + INC DE + DEC C + JP NZ, .next_row + POP BC + POP HL + INC L + DEC B ; dec width + JP NZ, get_image + JP img_task_end + +; --------------------------------------------------- +; Put image from buffer to VRAM +; Inp: HL -> VRAM[x,y] +; DE -> buffer +; BC - width, height +; --------------------------------------------------- +put_image: + PUSH HL + PUSH BC +.next_row: + ; two bytes for 8 pixels + ; byte 1 + LD A, (DE) ; get from buffer + LD (HL), A ; put to screen + INC H ; next Y (row) + INC DE ; next src addr + ; byte 2 + LD A, (DE) + LD (HL), A + INC DE + INC H + LD A, H + CP 128 ; last row? + JP NZ, .l2 + LD H, 0x40 ; reset +.l2: + DEC C + JP NZ, .next_row + POP BC + POP HL + ; next column + INC L + DEC B + JP NZ, put_image + +img_task_end: + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +fn39_sub2: + DEC C +.l1: + PUSH BC +.l2: + EX DE, HL + PUSH BC + PUSH HL + LD L, (HL) + LD H, 0x0 + LD A, (M_VARS.esc_var0) + LD B, A + OR A + JP Z, .l4 +.l3: + ADD HL, HL + DEC A + JP NZ, .l3 +.l4: + LD A, B + LD C, L + LD B, H + POP HL + INC HL + PUSH HL + LD L, (HL) + LD H, 0x0 + OR A + JP Z, .l6 +.l5: + ADD HL, HL + DEC A + JP NZ, .l5 +.l6: + EX DE, HL + LD A, (HL) + OR C + LD (HL), A + INC HL + LD A, (HL) + OR E + LD (HL), A + INC HL + LD (HL), B + INC HL + LD (HL), D + DEC HL + POP DE + INC DE + POP BC + DEC C + JP NZ, .l2 + POP BC + INC HL + INC HL + DEC B + JP NZ, .l1 + RET + +; -------------------------------------------------- +; Handle ASCII_CAN symbol (cursor right) +; -------------------------------------------------- +gih_rt: + DEC DE + LD A, (DE) + ADD A, 0x2 + LD (DE), A + CP 9 + RET C + LD A, 0x2 + LD (DE), A + INC DE + PUSH BC + LD H, D + LD L, E + INC HL + INC HL + LD A, (M_VARS.esc_var0) + PUSH AF + DEC A + LD (M_VARS.esc_var0), A + INC A + ADD A, A + ADD A, B + CP 128 + JP C, .l1 + SUB 0x40 +.l1: + LD B, A + LD A, (M_VARS.esc_var1) +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (HL) + LD (DE), A + INC HL + INC DE + LD A, (HL) + LD (DE), A + INC HL + INC DE + POP AF + DEC A + JP NZ, .l3 + LD A, (BC) + LD (DE), A + INC DE + INC HL + INC B + LD A, (BC) + LD (DE), A + INC HL + INC DE + DEC B + INC C + POP AF + DEC A + JP NZ, .l2 + POP AF + LD (M_VARS.esc_var0), A + POP BC + INC B + INC B + LD A, B + CP 0x80 + RET NZ + LD B, 0x40 + RET + +; -------------------------------------------------- +; Handle ASCII_BS (BackSpace) symbol +; -------------------------------------------------- +gih_bs: + DEC DE + LD A, (DE) + SUB 0x2 + LD (DE), A + RET NC + + LD A, 6 + LD (DE), A + INC DE + PUSH BC + ADD HL, DE + DEC HL + LD D, H + LD E, L + DEC HL + DEC HL + LD A, (M_VARS.esc_var1) + ADD A, C + DEC A + LD C, A + LD A, B + DEC A + CP 0x3f + JP NZ, .l1 + LD A, 0x7f ; [DEL]? +.l1: + LD B, A + LD A, (M_VARS.esc_var0) + PUSH AF + DEC A + LD (M_VARS.esc_var0), A + LD A, (M_VARS.esc_var1) +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + POP AF + DEC A + JP NZ, .l3 + LD A, (BC) + LD (DE), A + DEC HL + DEC DE + DEC B + LD A, (BC) + LD (DE), A + DEC HL + DEC DE + INC B + DEC C + POP AF + DEC A + JP NZ, .l2 + POP AF + LD (M_VARS.esc_var0), A + POP BC + DEC B + DEC B + LD A, B + CP 0x3e + RET NZ + LD B, 0x7e + RET + +; -------------------------------------------------- +; Handle ASCII_SUB symbol +; -------------------------------------------------- +gih_ctrl_z: + PUSH BC + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + ADD A, E + LD L, A + LD H, D + LD A, (M_VARS.esc_var1) + ADD A, C + LD C, A + PUSH BC + LD A, (M_VARS.esc_var1) + DEC A + DEC A + LD C, A +.l1: + LD A, (M_VARS.esc_var0) + LD B, A +.l2: + LD A, (HL) + LD (DE), A + INC HL + INC DE + LD A, (HL) + LD (DE), A + INC HL + INC DE + DEC B + JP NZ, .l2 + DEC C + JP NZ, .l1 + POP BC + LD L, 0x2 +.l3: + LD A, (M_VARS.esc_var0) + LD H, A + PUSH BC +.l4: + LD A, (BC) + LD (DE), A + INC DE + INC B + LD A, (BC) + LD (DE), A + INC DE + INC B + LD A, B + CP 0x80 + JP NZ, .l5 + LD B, 0x40 +.l5: + DEC H + JP NZ, .l4 + POP BC + INC C + DEC L + JP NZ, .l3 + POP BC + INC C + INC C + RET + +; -------------------------------------------------- +; Handle ASCII_EM symbol +; -------------------------------------------------- +gih_up: + PUSH BC + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, B + CP 128 + JP Z, .l1 + JP C, .l1 + SUB 64 +.l1: + DEC A + LD B, A + DEC C + PUSH BC + ADD HL, DE + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + LD E, A + LD A, L + SUB E + LD E, A + LD D, H + DEC DE + DEC HL + EX DE, HL + LD A, (M_VARS.esc_var1) + DEC A + DEC A + LD C, A +.l2: + LD A, (M_VARS.esc_var0) + LD B, A +.l3: + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + DEC B + JP NZ, .l3 + DEC C + JP NZ, .l2 + POP BC + LD L, 0x2 + +.l4: + LD A, (M_VARS.esc_var0) + LD H, A + PUSH BC + +.l5: + LD A, (BC) + LD (DE), A + DEC DE + DEC B + LD A, (BC) + LD (DE), A + DEC DE + DEC B + LD A, B + CP 0x3f + JP NZ, .l6 + LD B, 0x7f + +.l6: + DEC H + JP NZ, .l5 + POP BC + DEC C + DEC L + JP NZ, .l4 + POP BC + DEC C + DEC C + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +pict_sub2: + PUSH DE + DEC DE + LD A, (DE) + LD E, A + LD D, 0x0 + LD HL, (M_VARS.esc_param+1) + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) + POP HL + PUSH BC + PUSH HL + PUSH HL + LD HL, (M_VARS.esc_param+3) + INC HL + LD C, (HL) + INC HL + LD B, (HL) + POP HL + ADD HL, BC + LD C, L + LD B, H + POP HL + CALL mov_hl_bc + LD A, (M_VARS.esc_param+5) + OR A + JP Z, .l1 + EX DE, HL +.l1: + LD A, (M_VARS.esc_var1) + SUB 0x4 +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (DE) + INC DE + PUSH DE + PUSH AF + LD A, (DE) + LD E, A + POP AF + LD D, A + OR E + CPL + PUSH AF + AND (HL) + OR D + LD (BC), A + INC BC + INC HL + POP AF + AND (HL) + OR E + LD (BC), A + INC BC + INC HL + POP DE + INC DE + POP AF + DEC A + JP NZ, .l3 + POP AF + DEC A + JP NZ, .l2 + LD A, (M_VARS.esc_param+5) + OR A + JP Z, .l4 + EX DE, HL +.l4: + CALL mov_hl_bc + POP DE + EX DE, HL + LD A, (M_VARS.esc_var0) + LD C, A + LD A, (M_VARS.esc_var1) + LD B, A + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL put_image ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Move form [HL] to [BC] count of bytes +; Inp: HL -> src +; BC -> dst +; esc_var0*4 - count +; --------------------------------------------------- +mov_hl_bc: + PUSH DE + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + LD E, A ; E = param * 4 + ; move [HL] -> [BC] E bytes +.next: + LD A, (HL) + LD (BC), A + INC HL + INC BC + DEC E + JP NZ, .next + POP DE + RET + +; --------------------------------------------------- +; Draw circle +; Inp: param x,y,radius, aspect_x, aspect_y +; --------------------------------------------------- +esc_draw_circle: + LD A, (M_VARS.esc_param+2) ; radius + LD B, A + OR A + RET Z ; exit ir radius 0 + LD A, 0x7f + CP B + RET M ; exit if radius>127 + + XOR A + LD D, A ; 0 + LD E, B ; r + CALL dc_draw_8px + + LD A, 1 + LD H, A + SUB B + LD C, A + LD A, B + RLCA + LD B, A + LD A, 0x1 + SUB B + LD L, A + CCF ; TODO: unused +.l1: + INC D + LD A, E + CP D + JP Z, dc_draw_8px + CALL dc_draw_8px + LD A, H + ADD A, 0x2 + LD H, A + LD A, L + ADD A, 0x2 + LD L, A + LD A, C + ADD A, H + LD C, A + JP NC, .l1 +.l2: + CCF ; TODO: unused + INC D + DEC E + LD A, D + CP E + JP Z, dc_draw_8px + SUB E + CP 0x1 + RET Z + LD A, E + SUB D + CP 0x1 + JP Z, dc_draw_8px + CALL dc_draw_8px + LD A, H + ADD A, 0x2 + LD H, A + LD A, L + ADD A, 0x4 + LD L, A + JP NC, .l3 + CCF ; TODO: unused +.l3: + LD A, C + ADD A, L + LD C, A + JP NC, .l1 + JP .l2 + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_8px: + PUSH HL + PUSH DE + PUSH BC + PUSH DE + CALL dc_aspect_ratio_1 + LD HL, (M_VARS.esc_param) ; HL=Y,X + CALL dc_draw_4px_bc + POP DE + CALL dc_aspect_ratio2 + LD HL, (M_VARS.esc_param) ; HL=Y,X + CALL dc_draw_4px_cb + POP BC + POP DE + POP HL + XOR A + RET + +; --------------------------------------------------- +; Scale circle axis dy specified aspect ratio +; if aspect_x = 0 C = D else C = D * aspect_x / 256 +; if aspect_y = 0 B = E else B = E * aspect_y / 256 +; --------------------------------------------------- +dc_aspect_ratio_1: + LD HL, (M_VARS.esc_param+3) ; aspect_x -> L, aspect_y -> H + LD A, L + OR A + LD C, D + LD B, E + JP NZ, .dc_ax_ne0 + LD A, H + OR A + JP NZ, .dc_ay_ne0 + RET +.dc_ax_ne0: + LD A, H + LD H, L + LD E, C + CALL dc_mul_e_h + LD C, E + OR A + RET Z +.dc_ay_ne0: + LD H, A + LD E, B + CALL dc_mul_e_h + LD B, E + RET + +; --------------------------------------------------- +; if aspect_x = 0 B = E else B = E * aspect_x / 256 +; if aspect_y = 0 C = D else C = D * aspect_y / 256 +; --------------------------------------------------- +dc_aspect_ratio2: + LD HL, (M_VARS.esc_param+3) ; aspect_x -> L, aspect_y -> H + LD A, L + OR A + LD C, D + LD B, E + JP NZ, .dc_ax_ne0 + LD A, H + OR A + JP NZ, .dc_ay_ne0 + RET +.dc_ax_ne0: + LD A, H + LD H, L + LD E, B + CALL dc_mul_e_h + LD B, E + OR A + RET Z + +.dc_ay_ne0: + LD H, A + LD E, C + CALL dc_mul_e_h + LD C, E + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_mul_e_h: + LD D, 0x0 + LD L, D + ADD HL, HL + JP NC, .l1 + ADD HL, DE +.l1: + ADD HL, HL + JP NC, .l2 + ADD HL, DE +.l2: + ADD HL, HL + JP NC, .l3 + ADD HL, DE +.l3: + ADD HL, HL + JP NC, .l4 + ADD HL, DE +.l4: + ADD HL, HL + JP NC, .l5 + ADD HL, DE +.l5: + ADD HL, HL + JP NC, .l6 + ADD HL, DE +.l6: + ADD HL, HL + JP NC, .l7 + ADD HL, DE +.l7: + ADD HL, HL + JP NC, .l8 + ADD HL, DE +.l8: + LD E, H + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_4px_bc: + ; draw pixel(H+B, L+C) if in screen + LD A, H + ADD A, B + JP C, .l1 + LD D, A + LD A, L + ADD A, C + LD E, A + CALL dc_put_pixel +.l1: + ; draw pixel(H+B, L-C) if in screen + LD A, H + ADD A, B + JP C, .l2 + LD D, A + LD A, L + SUB C + LD E, A + CALL dc_put_pixel +.l2: + ; draw pixel(H-B, L-C) if in screen + LD A, H + SUB B + JP C, .l3 + LD D, A + LD A, L + SUB C + LD E, A + CALL dc_put_pixel +.l3: + ; draw pixel(H-B, L+C) if in screen + LD A, H + SUB B + RET C + LD D, A + LD A, L + ADD A, C + LD E, A + CALL dc_put_pixel ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_4px_cb: + ; draw pixel(H+C, L+B) if in screen + LD A, H + ADD A, C + JP C, .l1 + LD D, A + LD A, L + ADD A, B + LD E, A + CALL dc_put_pixel +.l1: + ; draw pixel(H+C, L-B) if in screen + LD A, H + ADD A, C + JP C, .l2 + LD D, A + LD A, L + SUB B + LD E, A + CALL dc_put_pixel +.l2: + ; draw pixel(H-C, L-B) if in screen + LD A, H + SUB C + JP C, l3 + LD D, A + LD A, L + SUB B + LD E, A + CALL dc_put_pixel +l3: + ; draw pixel(H-C, L+B) if in screen + LD A, H + SUB C + RET C + LD D, A + LD A, L + ADD A, B + LD E, A + CALL dc_put_pixel ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Draw pixel on screen +; Inp: DE - X, Y +; --------------------------------------------------- +dc_put_pixel: + RET C ; return if CF set (out of screen) + PUSH HL + PUSH BC + CALL calc_px_addr + ; calculate B = pixel mask + LD A, 10000000b +.roll: + RLCA ; [07654321] <- [76547210], [7] -> CF + DEC B + JP P, .roll + CPL + LD B, A + ; DE = foreground color low and hi bytes + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Turn on Video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; Load VRAM[HL] byte (low byte), mask and set + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; Load VRAM[HL+1] byte (low byte), mask and set + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; Turn off Video RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + POP BC + POP HL + RET + + ; Full charset, Common + Latin letters (112*8=890) + INCLUDE "font-6x7.inc" + +; --------------------------------------------------- +; Convert 0h..Fh decimal value to symbol '0'..'F' +; --------------------------------------------------- +conv_nibble: + AND 0xf + ADD A, 0x90 + DAA + ADC A, 0x40 + DAA + LD C, A + RET + +; --------------------------------------------------- +; Print byte in HEX +; Inp: A - byte to print +; --------------------------------------------------- +m_hexb: + PUSH AF + RRCA + RRCA + RRCA + RRCA + CALL out_hex + POP AF + +out_hex: + CALL conv_nibble + CALL m_con_out ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Wtite RAM-Disk 64K to TAPE +; --------------------------------------------------- +m_tape_write_ram2: + LD HL, M_VARS.buffer + LD C, 128 +.cl_stack: + LD (HL), 0x0 + INC HL + DEC C + JP NZ, .cl_stack + LD HL, M_VARS.buffer + LD DE, 0xffff + ; write empty block + ; DE - block ID + ; HL -> block + CALL m_tape_write + CALL twr2_delay + LD DE, 0x0 + CALL m_tape_write + CALL twr2_delay + LD BC, 512 + LD DE, 0x0 +.nxt_blk: + PUSH BC + LD HL, M_VARS.buffer + CALL m_ramdisk_read + INC DE + CALL m_tape_write + CALL twr2_delay + POP BC + DEC BC + LD A, B + OR C + JP NZ, .nxt_blk + RET + +; --------------------------------------------------- +; Pause between blocks on tape +; --------------------------------------------------- +twr2_delay: + LD BC, 250 +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + RET + +; --------------------------------------------------- +; Read RAM-Disk 64K from TAPE +; --------------------------------------------------- +m_tape_read_ram2: + LD A, 100 + CALL m_tape_wait + OR A + JP NZ, .end + LD E, 6 + +.srch_first: + DEC E + JP Z, .not_found + ; read block + LD HL, M_VARS.buffer + CALL m_tape_read + CP 4 + JP Z, .end + OR A + JP NZ, .srch_first + LD A, B + OR C + JP NZ, .srch_first + + LD BC, 512 + LD DE, 0x0 + +.rd_next: + PUSH BC + ; Read block from tape + CALL m_tape_read + OR A + JP NZ, .rd_error + DEC BC + LD A, B + CP D + JP NZ, .inv_id + LD A, C + CP E + JP NZ, .inv_id + ; Ok, write block to RAM disk + CALL m_ramdisk_write + INC DE + POP BC + DEC BC + LD A, B + OR C + JP NZ, .rd_next + RET +.not_found: + LD HL, msg_no_start_rec + CALL me_out_strz ; TODO: replace call+ret to jp + RET +.rd_error: + CP 2 + JP Z, .err_ubi + CP 4 + JP Z, .err_ibu + LD HL, msg_checksum + CALL me_out_strz + CALL out_hexw + POP BC + RET + + ; Illegal sequence of blocks +.inv_id: + LD HL, msg_sequence + CALL me_out_strz + INC BC + CALL out_hexw + POP BC + RET + +.err_ubi: + LD HL, msg_ibg + CALL me_out_strz + POP BC + RET + + ; Interrupted by user +.err_ibu: + POP BC +.end: + LD HL, msg_break + CALL me_out_strz ; TODO: replace call+ret to jp + RET + +; -------------------------------------------------- +; Output hex word +; Inp: BC - word to output +; -------------------------------------------------- +out_hexw: + PUSH BC + LD A, B + CALL m_hexb + POP BC + LD A, C + CALL m_hexb ; TODO: replace call+ret to jp + RET + +msg_no_start_rec: + DB "NO START RECORD", 0 +msg_checksum: + DB "CHECKSUM ", 0 +msg_sequence: + DB "SEQUENCE ", 0 +msg_ibg: + DB "IBG", 0 +msg_break: + DB "BREAK", 0 + +; --------------------------------------------------- +; Out ASCIIZ message +; Inp: HL -> zero ended string +; --------------------------------------------------- +me_out_strz: + LD A, (HL) + OR A + RET Z + PUSH BC + LD C, A + CALL m_con_out + INC HL + POP BC + JP me_out_strz + + + +; --------------------------------------------------- +; Read from RAM-disk to RAM +; Inp: DE - source sector +; HL -> destination buffer +; --------------------------------------------------- +m_ramdisk_read: + PUSH HL + PUSH DE + LD A, D + ; Build value to access ext RAM (A17, A16, 32k bits) + AND 00000111b ; A17, A16, Low 32K bits of memory mapper + ADD 0x2 ; Calc A16, A17 address lines + OR 0x0 ; TODO: nothing, remove + LD B, A ; B - value to turn on access to Ext RAM + ; Calculate DE = address from sector number + XOR A + LD A, E ; E - low address + RRA ; [CF] -> [7:0] -> [CF] + LD D, A ; D = E/2 + LD A, 0x0 + RRA ; [CF] -> E + LD E, A +.read: + ; Access to ExtRAM + LD A, B + OUT (SYS_DD17PB), A + ; Get Byte + LD A, (DE) + LD C, A + ; Access to RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + ; Set Byte + LD (HL), C + ; HL++, DE++ + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, .read ; jump if has more bytes + + ; Access to RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP DE + POP HL + RET + +; --------------------------------------------------- +; Write sector to RAM disk +; Inp: HL -> source buffer +; DE - destination sector +; --------------------------------------------------- +m_ramdisk_write: + PUSH HL + PUSH DE + LD A, D + AND 0x7 + ADD 0x2 ; build value to access ext RAM (A17, A16, 32k bits) + OR 0x0 ; TODO: remove unused + LD B, A + XOR A + LD A, E + RRA + LD D, A + LD A, 0x0 + RRA + LD E, A +.wr_byte: + LD A, 0x0 + OUT (SYS_DD17PB), A + LD C, (HL) + LD A, B + OUT (SYS_DD17PB), A + LD A, C + LD (DE), A + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, .wr_byte + LD A, 0x0 + OUT (SYS_DD17PB), A + POP DE + POP HL + RET + +; -------------------------------------------------- +; Write block to Tape +; In: DE - block ID, +; HL -> block of data. +; -------------------------------------------------- +m_tape_write: + PUSH HL + PUSH DE + PUSH DE + LD BC, 2550 + LD A, PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A,TMR0_SQWAVE ; tmr0, load lsb+msb, sq wave, bin + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A, B + OUT (TMR_DD70C1), A + ; Write Hi+Lo, Hi+Lo + LD DE, 4 ; repeat next 4 times +.l1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; check rst4 from timer#0 + JP NZ, .l1 + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH ; tape level hi + JP NZ, .set_lvl + LD A, TL_LOW ; tape level low +.set_lvl: + OUT (DD67PC), A ; set tape level + LD A, TMR0_SQWAVE ; tmr0, load lsb+msb, swq, bin + ; timer on + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A, B + OUT (TMR_DD70C1), A + DEC E + JP NZ, .l1 + +.l2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .l2 + + ; Write 00 at start + LD A, 0x0 + CALL m_tape_wr_byte + ; Write 0xF5 marker + LD A, 0xf5 + CALL m_tape_wr_byte + LD E, 0x0 ; checksum=0 + ; Write block ID + POP BC + LD A, C + CALL m_tape_wr_byte + LD A, B + CALL m_tape_wr_byte + ; Write 128 data bytes + LD B, 128 +.next_byte: + LD A, (HL) + CALL m_tape_wr_byte + INC HL + DEC B + JP NZ, .next_byte + ; Write checksum + LD A, E + CALL m_tape_wr_byte + ; Write final zero byte + LD A, 0x0 + CALL m_tape_wr_byte +.wait_end: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .wait_end + LD A, TL_MID ; tape level middle + OUT (DD67PC), A + POP DE + POP HL + RET + + +; ------------------------------------------------------ +; Write byte to tape +; Inp: A - byte top write +; D - current level +; E - current checksum +; ------------------------------------------------------ +m_tape_wr_byte: + PUSH BC + ; calc checksum + LD B, A + LD A, E + SUB B + LD E, A + LD C, 8 ; 8 bit in byte +.get_bit: + LD A, B + RRA + LD B, A + JP C, .bit_hi +.wait_t: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .wait_t + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; program for 360 cycles + LD A, 0x68 + OUT (TMR_DD70C1), A + LD A, 0x1 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH + JP NZ, .out_bit + LD A, TL_LOW +.out_bit: + OUT (DD67PC), A + DEC C + JP NZ,.get_bit + POP BC + RET +.bit_hi: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .bit_hi + ; program for 660 cycles + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + LD A, 0x94 + OUT (TMR_DD70C1), A + LD A, 0x2 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH + JP NZ, .out_bit_hi + LD A, TL_LOW +.out_bit_hi: + OUT (DD67PC), A + DEC C + JP NZ, .get_bit + POP BC + RET + +; ------------------------------------------------------ +; Load block from Tape +; In: HL -> buffer to receive bytes from Tape +; Out: A = 0 - ok, +; 1 - CRC error, +; 2 - unexpected block Id +; 4 - key pressed +; ------------------------------------------------------ +m_tape_read: + PUSH HL + PUSH DE + LD A, PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A ; tmr0, load lsb+msb, sq wave + LD A, 0x0 + ; tmr0 load 0x0000 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + LD C, 3 +.wait_3_changes: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + LD A, B + ADD A, 4 + JP P, .wait_3_changes + DEC C + JP NZ, .wait_3_changes +.wait_4th_change: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + LD A, B + ADD A, 4 + JP M, .wait_4th_change + LD C, 0x0 +.wait_f5_marker: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + DEC A + RRA + LD A, C + RRA + LD C, A + CP 0xf5 + JP NZ, .wait_f5_marker + LD E, 0x0 ; checksum = 0 + ; Read blk ID + CALL m_tape_read_byte + JP NC, .err_read_id + LD C, D + CALL m_tape_read_byte + JP NC, .err_read_id + LD B, D + PUSH BC + ; Read block, 128 bytes + LD C, 128 +.read_next_b: + CALL m_tape_read_byte + JP NC, .err_read_blk + LD (HL), D + INC HL + DEC C + JP NZ, .read_next_b + + ; Read checksum + CALL m_tape_read_byte + JP NC, .err_read_blk + LD A, E + OR A + JP Z, .checksum_ok + LD A, 0x1 ; bad checksum +.checksum_ok: + POP BC +.return: + POP DE + POP HL + RET + +.err_read_blk: + POP BC + LD BC, 0x0 +.err_read_id: + LD A, 0x2 ; read error + JP .return +.key_pressed: + CALL m_con_in + LD C, A ; store key code in C + LD B, 0x0 + LD A, 0x4 + JP .return + +; ------------------------------------------------------ +; Read byte from Tape +; Out: D - byte +; CF is set if ok, cleared if error +; ------------------------------------------------------ +m_tape_read_byte: + PUSH BC + LD C, 8 +.next_bit: + CALL m_read_tape_bit + ; push bit from lo to hi in D + RRA + LD A, D + RRA + LD D, A + LD A, 4 + ADD A, B + JP NC, .ret_err + DEC C + JP NZ, .next_bit + ; calc checksum + LD A, D + ADD A, E + LD E, A + SCF +.ret_err: + POP BC + RET + +; ------------------------------------------------------ +; Read bit from tape +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +m_read_tape_bit: + IN A, (KBD_DD78PB) ; Read Tape bit 5 (data) + AND TAPE_P + LD B, A +.wait_change: + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z, .wait_change + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; [360...480...660] 0x220=544d + IN A, (TMR_DD70C1) ; get tmer#0 lsb + ADD A, 0x20 + IN A, (TMR_DD70C1) ; get tmer#0 msb + LD B, A + ADC A, 0x2 + ; reset timer to 0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; For 0 - 65535-360+544 -> overflow P/V=1 + ; For 1 - 65535-660+544 -> no overflow P/V=0 + RET P + INC A + RET + +; ------------------------------------------------------ +; Read bit from tape with keyboard interruption +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +read_tape_bit_kbd: + IN A, (KBD_DD78PB) + AND TAPE_P + LD B, A ; save tape bit state + ; wait change with keyboard check +.wait_change: + IN A, (PIC_DD75RS) + AND KBD_IRQ + JP NZ, .key_pressed + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z, .wait_change + ; measure time + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; read lsb+msb + IN A, (TMR_DD70C1) + ADD A, 0x20 + IN A, (TMR_DD70C1) + LD B, A + ADC A, 0x2 + ; reset timer#0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; flag P/V is set for 0 + RET P + INC A + RET +.key_pressed: + LD A, 0xff + RET + +; ------------------------------------------------------ +; Wait tape block +; Inp: A - periods to wait +; Out: A=4 - interrupded by keyboard, C=key +; ------------------------------------------------------ +m_tape_wait: + OR A + RET Z + PUSH DE + LD B, A +.wait_t4: + LD C,B + IN A, (KBD_DD78PB) + AND TAPE_P ; Get TAPE4 (Wait det) and save + LD E, A ; store T4 state to E +.wait_next_2ms: + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; load 3072 = 2ms + XOR A + OUT (TMR_DD70C1), A + LD A, 0xc + OUT (TMR_DD70C1), A +.wait_tmr_key: + IN A, (PIC_DD75RS) + AND KBD_IRQ ; RST1 flag (keyboard) + JP NZ, .key_pressed + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; RST4 flag (timer out) + JP Z, .wait_no_rst4 + IN A, (KBD_DD78PB) + AND TAPE_P ; TAPE4 not changed? + CP E + JP NZ, .wait_t4 ; continue wait + JP .wait_tmr_key +.wait_no_rst4: + DEC C + JP NZ, .wait_next_2ms + XOR A + POP DE + RET + +.key_pressed: + CALL m_con_in + LD C, A ; C = key pressed + LD A, 0x4 ; a=4 interrupted by key + POP DE + RET + +; ------------------------------------------------------ +; Check block marker from Tape +; Out: A=0 - not detected, 0xff - detected +; ------------------------------------------------------ +m_tape_blk_detect: + IN A, (KBD_DD78PB) + AND TAPE_D ; TAPE5 - Pause detector + LD A, 0x0 + RET Z + CPL + RET + +; ====================================================== +; FDC DRIVER +; ====================================================== + + +fdc_unload_head: + LD A, 0x0 + OUT (FDC_DATA), A + LD A, FDC_RESTORE_UH_NV + OUT (FDC_CMD), A + NOP + NOP + +.wait_no_busy: + IN A, (FDC_CMD) + AND 00000101b ; Track0 , Busy + CP 00000100b + JP Z, .tr0_ok + + IN A, (FLOPPY) + RLCA ; MOTST -> CF + JP NC, .wait_no_busy + LD A, 0x20 + RET +.tr0_ok: + LD A, B + DEC A + LD A, 0x1 + JP Z, .b1 + LD (M_VARS.ul_A_var1), A + XOR A + LD (M_VARS.ul_A_var4), A + RET + +.b1: + LD (M_VARS.ul_B_var2), A + XOR A + LD (M_VARS.ul_B_var5), A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_select_drive: + PUSH AF + CALL delay_1.4mS + CP 0x1 ; TODO: DEC A to save 1b 3t + JP Z, .sel_A + LD A, 0x2 + JP .sel_B +.sel_A + LD A, 0x5 +.sel_B + LD B, A ; 010b or 101b + IN A, (FLOPPY) ; read Floppy controller reg + AND 0x40 ; SSEL + RRA ; SSEL for out + OR B ; 0x22 or 0x25 if WP + OUT (FLOPPY), A ; Select drive A or B + LD B, A + POP AF + DEC A + JP Z, .dpar_a + LD A, (bios_var2) + JP .dpar_b +.dpar_a + LD A, (BIOS.bios_var04) +.dpar_b + PUSH BC + LD B, A ; B = dpar_A or B + LD A, D ; compare with D? + CP B + JP C, .l_le ; D < B + SUB B + LD D, A + POP BC + LD A, C + OR 0x8 + LD C, A + LD A, B + AND 0x20 + OR A + RET NZ + LD A, B + OR 0x20 ; set SSEL to 1 + OUT (FLOPPY), A + CALL delay_136uS + RET +.l_le + POP BC + LD A, B + AND 0x20 + OR A + RET Z + LD A, B + AND 0x7 + OUT (FLOPPY), A + CALL delay_136uS + RET + +; --------------------------------------------------- +; Delay for 136uS +; --------------------------------------------------- +delay_136uS: + LD B, 16 ; 7 + +; --------------------------------------------------- +; Delay for B*8uS +; --------------------------------------------------- +delay_b: + DEC B ; 4 + JP NZ, delay_b ; 10 + RET ; 10 + +; --------------------------------------------------- +; Delay for 1.4mS +; --------------------------------------------------- +delay_1.4mS: + LD B, 175 ; 7 + JP delay_b ; 10 + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_read_floppy: + PUSH AF + CALL m_select_drive + POP AF + CALL m_start_seek_track + JP C, fdc_ret + CALL m_fdc_read_c_bytes + JP C, fdc_ret + XOR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_write_floppy: + PUSH AF + CALL m_select_drive + POP AF + CALL m_start_seek_track + JP C, fdc_ret + CALL m_fdc_write_bytes + JP C, fdc_ret + XOR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_start_seek_track: + CALL m_start_floppy + RET C + CALL m_fdc_seek_trk + RET C + RET ; TODO: remove + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_start_floppy: + LD B, A + LD A, (M_VARS.ul_var3) + CP B + JP Z, .need_m_start + CALL .wait_motor ; TODO: replace call+ret to jp + RET +.need_m_start: + IN A, (FLOPPY) + RLCA ; check MOTST + JP C, .wait_motor + IN A, (FDC_CMD) + AND FDC_NOT_READY ; not ready flag + RET Z + +; --------------------------------------------------- +; +; --------------------------------------------------- +.wait_motor: + PUSH BC + LD BC, 65535 + CALL fdc_init +.wait_rdy1: + IN A, (FDC_CMD) + AND FDC_NOT_READY + JP Z, .long_delay + IN A, (FLOPPY) + RLCA ; CF<-A[7] MOTST flag + JP NC, .wait_rdy1 + LD A, 0x20 + JP .mst_exi +.long_delay: + DEC BC + LD A, B + OR A + JP NZ, .long_delay +.mst_exi: + POP BC + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +fdc_init: + IN A, (FLOPPY) + AND 01001110b ; Get SSEL, DRSEL, MOT1, MOT0 + RRA + OUT (FLOPPY), A + OR 0x08 ; Set INIT bit + OUT (FLOPPY), A + RET + +; --------------------------------------------------- +; Seek track on floppy +; Inp: DE - track/sector +; --------------------------------------------------- +m_fdc_seek_trk: + LD A, B + DEC A + JP Z, .drv_b + LD A, (M_VARS.ul_A_var1) + OR A + CALL Z, fdc_unload_head + RET C + LD A, (M_VARS.ul_A_var4) + OUT (FDC_TRACK), A + LD A, D + LD (M_VARS.ul_A_var4), A + JP .cmn +.drv_b: + LD A, (M_VARS.ul_B_var2) + OR A + CALL Z, fdc_unload_head + RET C + LD A, (M_VARS.ul_B_var5) + OUT (FDC_TRACK), A + LD A, D + LD (M_VARS.ul_B_var5), A +.cmn: + LD A, (M_VARS.ul_var3) + CP B + LD A, B + LD (M_VARS.ul_var3), A + JP NZ, .l2 + IN A, (FDC_TRACK) + CP D + JP Z, .l2 + JP C, .l1 + LD A, (M_VARS.ul_var6) + OR A + JP NZ, .l2 + LD B, 0xff + CALL delay_b + LD A, 0x1 + LD (M_VARS.ul_var6), A + JP .l2 +.l1: + LD A, (M_VARS.ul_var6) + OR A + JP Z, .l2 + LD B, 0xff + CALL delay_b + LD A, 0x0 + LD (M_VARS.ul_var6), A +.l2: + LD A, D + OUT (FDC_DATA), A + LD A, 0x1f + OUT (FDC_CMD), A + NOP + NOP + IN A, (FDC_WAIT) + IN A, (FDC_CMD) + AND 0x19 + CP 0x0 + JP NZ, .l3 + JP .l4 +.l3: + SCF + LD A, 0x40 +.l4: + PUSH AF + LD A, E + OUT (FDC_SECT), A + POP AF + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_fdc_write_bytes: + LD A, C + OUT (FDC_CMD), A +.w_next: + IN A, (FDC_WAIT) + RRCA + LD A, (HL) + OUT (FDC_DATA), A + INC HL + JP C, .w_next + CALL fdc_check_status ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_fdc_read_c_bytes: + LD A, C + OUT (FDC_CMD), A + JP .l2 +.l1: + LD (HL), A + INC HL +.l2: + IN A, (FDC_WAIT) + RRCA + IN A, (FDC_DATA) + JP C, .l1 + CALL fdc_check_status ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Check fdc status for errors +; Out: CF set if errors +; --------------------------------------------------- +fdc_check_status: + IN A, (FDC_CMD) + AND 11011111b + CP 0x0 + JP Z, fdc_ret + SCF +fdc_ret: + RET + +filler1: + DB 20h + +; filler: +; ds 169, 0xff + +; ------------------------------------------------------ + +LAST EQU $ +CODE_SIZE EQU LAST-0xE000 +FILL_SIZE EQU ROM_CHIP_SIZE-CODE_SIZE + + + ASSERT m_hot_start = 0xe051 + ASSERT m_out_strz = 0xe0f1 + ASSERT m_con_out_int = 0xe16d + ASSERT get_esc_param = 0xe187 + ASSERT esc_params_tab = 0xe1cb + ASSERT esc_handler_tab = 0xe1db + ASSERT esc_set_beep = 0xe1f9 + ASSERT m_print_hor_line = 0xe23a + ASSERT m_get_7vpix = 0xe2b4 + ASSERT esc_set_palette = 0xe2fe + ASSERT m_get_glyph = 0xe320 + ASSERT m_print_no_esc = 0xe349 + ASSERT calc_addr_40 = 0xe439 + ASSERT mp_mode_64 = 0xe4b8 + ASSERT calc_addr_80 = 0xe612 + ASSERT m_clear_screen = 0xe639 + ASSERT m_cursor_home = 0xe66c + ASSERT m_draw_cursor = 0xe69a + ASSERT m_handle_esc_code = 0xe77c + ASSERT handle_cc_common = 0xe7c4 + ASSERT handle_cc_80x25 = 0xe833 + ASSERT m_beep = 0xe85a + ASSERT esc_set_cursor = 0xe890 + ASSERT esc_set_vmode = 0xe8e9 + ASSERT esc_set_color = 0xe92f + ASSERT m_print_at_xy = 0xe943 + ASSERT game_sprite_tab = 0xea39 + ASSERT esc_draw_fill_rect = 0xeb64 + ASSERT draw_line_h = 0xeed1 + ASSERT esc_draw_line = 0xef0b + ASSERT esc_draw_dot = 0xf052 + ASSERT esc_picture = 0xf0a4 + ASSERT m_fn_39 = 0xf10f + ASSERT get_image_hdr = 0xf177 + ASSERT esc_get_put_image = 0xf1b5 + ASSERT pict_sub2 = 0xf3ca + ASSERT m_font_cp0 = 0xf5bc + ASSERT me_out_strz = 0xfb36 + ASSERT m_ramdisk_write = 0xfb6d + ASSERT m_tape_write = 0xfb97 + ASSERT m_tape_wr_byte = 0xfc0e + ASSERT m_tape_read_byte = 0xfcee + ASSERT m_read_tape_bit = 0xfd08 + ASSERT m_tape_wait = 0xfd58 + + + ; DISPLAY "Code size is: ", /A, CODE_SIZE + + +FILLER + DS FILL_SIZE, 0xFF + ; DISPLAY "Free size is: ", /A, FILL_SIZE + + ENDMODULE + + OUTEND + + OUTPUT m_vars.bin + ; put in separate waste file + INCLUDE "m_vars.inc" + OUTEND diff --git a/MON_v8_3edeb015/ram.inc b/MON_v8_3edeb015/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/MON_v8_3edeb015/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/MON_v8_c4eec374/.gitignore b/MON_v8_c4eec374/.gitignore new file mode 100644 index 0000000..b626b35 --- /dev/null +++ b/MON_v8_c4eec374/.gitignore @@ -0,0 +1,10 @@ +*.labels +*.obj +*.OBJ +*.bin +*.tmp +tmp/ +build/ +*.lst +*.sld + diff --git a/MON_v8_c4eec374/.vscode/extensions.json b/MON_v8_c4eec374/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/MON_v8_c4eec374/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/MON_v8_c4eec374/.vscode/tasks.json b/MON_v8_c4eec374/.vscode/tasks.json new file mode 100644 index 0000000..9c09341 --- /dev/null +++ b/MON_v8_c4eec374/.vscode/tasks.json @@ -0,0 +1,34 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "make MONITOR (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=monitor.sld", + "--sym=monitor.labels", + "--raw=monitor.obj", + "--fullpath", + "monitor.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} \ No newline at end of file diff --git a/MON_v8_c4eec374/BIN/MON_V8_c4eec374.BIN b/MON_v8_c4eec374/BIN/MON_V8_c4eec374.BIN new file mode 100644 index 0000000..203b064 Binary files /dev/null and b/MON_v8_c4eec374/BIN/MON_V8_c4eec374.BIN differ diff --git a/MON_v8_c4eec374/README.md b/MON_v8_c4eec374/README.md new file mode 100644 index 0000000..5beee09 --- /dev/null +++ b/MON_v8_c4eec374/README.md @@ -0,0 +1,16 @@ +# Ocean-240.2 ROM Monitor V8 checksum c4eec374 + +Source codes of Monitor v8 for Ocean-240.2 with Floppy controller. +In Z80 mnemonics, but limited for i8080 instruction set. + +## Differences: + +Other versions of the code are compared with this one. + +## Compile: + +Code is located in memory at address: 0xE000..0xFFFF + + sjasmplus --sld=monitor.sld --sym=monitor.labels --raw=monitor.obj --fullpath monitor.asm + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/MON_v8_c4eec374/bios_entries.inc b/MON_v8_c4eec374/bios_entries.inc new file mode 100644 index 0000000..117a2c8 --- /dev/null +++ b/MON_v8_c4eec374/bios_entries.inc @@ -0,0 +1,41 @@ + +; ======================================================= +; Ocean-240.2 +; Definition of CPM BIOS entries to compile depended +; modules +; +; By Romych 2025-09-09 +; ====================================================== + IFNDEF _BIOS + DEFINE _BIOS + + MODULE BIOS + +boot_f EQU 0xD600 +wboot_f EQU 0xD603 +const_f EQU 0xD606 +conin_f EQU 0xD609 +conout_f EQU 0xD60C +list_f EQU 0xD60F +punch_f EQU 0xD612 +reader_f EQU 0xD615 +home_f EQU 0xD618 +seldsk_f EQU 0xD61B +settrk_f EQU 0xD61E +setsec_f EQU 0xD621 +setdma_f EQU 0xD624 +read_f EQU 0xD627 +write_f EQU 0xD62A +sectran_f EQU 0xD630 +reserved_f1 EQU 0xD633 +reserved_f2 EQU 0xD636 +tape_read_f EQU 0xD639 +tape_write_f EQU 0xD63C +tape_wait_f EQU 0xD63F + +bios_var04 EQU 0xD64B + + ENDMODULE + + + ENDIF diff --git a/MON_v8_c4eec374/equates.inc b/MON_v8_c4eec374/equates.inc new file mode 100644 index 0000000..b1b5622 --- /dev/null +++ b/MON_v8_c4eec374/equates.inc @@ -0,0 +1,149 @@ +; ====================================================== +; Ocean-240.2 +; Equates for all assembly sources +; +; By Romych 2025-09-09 +; ====================================================== + + IFNDEF _EQUATES + DEFINE _EQUATES + +ADDLIST EQU 0x08 +ASCII_BELL EQU 0x07 ; Make Beep +ASCII_BS EQU 0x08 ; Move cursor left (Back Space) +ASCII_TAB EQU 0x09 ; Move cursor right +8 pos +ASCII_LF EQU 0x0A ; Move cursor down (Line Feed) +ASCII_FF EQU 0x0C ; Move cursor to home (Form Feed) +ASCII_CR EQU 0x0D ; Move gursor to 1st pos (Cariage Return) +ASCII_CAN EQU 0x18 ; Move cursor right +ASCII_EM EQU 0x19 ; Move cursor up +ASCII_SUB EQU 0x1A ; CTRL-Z - end of text file marker +ASCII_ESC EQU 0x1B ; +ASCII_US EQU 0x1F ; Clear screen +ASCII_SP EQU 0x20 +ASCII_DEL EQU 0x7F + +; ------------------------------------------------------ +BDOS_NFUNCS EQU 41 +BELL_PIN EQU 0x08 ; DD67 Pin PC3 - "BELL" +; ------------------------------------------------------ +CCP_COMMAND_SIZE EQU 5 ; max length of CCP command +CCP_COMMAND_COUNT EQU 3 ; Count of CCP commands +CCP_COMMAND_CNT EQU 6 +CCP_COMMAND_LEN EQU 4 + +CCP_SRC_ADDR EQU 0xc000 ; Address of CCP resident part in ROM +CCP_DST_ADDR EQU 0xb200 ; Address of CCP resident part in RAM +CCP_SIZE EQU 0x809 + +CPM_VERSION EQU 0x22 ; Version of CP/M as byte nibbles '2.2' + +CTRL EQU 0x5E ; ^ +CTRL_C EQU 0x03 ; Warm boot +CTRL_H EQU 0x08 ; Backspace +CTRL_E EQU 0x05 ; Move to beginning of new line (Physical EOL) +CTRL_J EQU 0x0A ; LF - Line Feed +CTRL_M EQU 0x0D ; CR - Carriage Return +CTRL_P EQU 0x10 ; turn on/off printer +CTRL_R EQU 0x12 ; Repeat current cmd line +CTRL_S EQU 0x13 ; Temporary stop display data to console (aka DC3) +CTRL_U EQU 0x15 ; Cancel (erase) current cmd line +CTRL_X EQU 0x18 ; Cancel (erase) current cmd line + +; ------------------------------------------------------ +DBPLIST EQU 0x0F +DEF_DISK_A_SIZE EQU 0x3F +DEF_DISK_B_SIZE EQU 0x0168 +DIR_BUFF_SIZE EQU 128 +DSK_MAP EQU 0x10 +DSK_MSK EQU 0x03 +DSK_SHF EQU 0x02 +DMA_BUFF_SIZE EQU 0x80 +; ------------------------------------------------------ +ENDDIR EQU 0xFFFF +ESC_CMD_END EQU 0x1A +;EXT_NUM EQU 12 ; Extent byte offset +; ------------------------------------------------------ +FALSE EQU 0x00 +FDC_DD80RB EQU 0x21 +FDC_NOT_READY EQU 0x80 +FDC_RESTORE_L EQU 0x08 +FDC_SEEK_LV EQU 0x1C +FDC_RESTORE_UH_NV EQU 0x03 ; Restore Unload Head, No Verify, 15ms Rate +FILE_DELETED EQU 0xE5 +FWF_MASK EQU 0x80 ; File Write Flag mask + +; --------------------------------------------------- +; FCB Offsets +; --------------------------------------------------- +FCB_LEN EQU 32 ; length of FCB +FCB_SHF EQU 5 +FN_LEN EQU 12 ; Length of filename in FCB + +FCB_DR EQU 0 ; Drive. 0 for default, 1-16 for A-P +FCB_FN EQU 1 ; Fn - Filename, 7-bit ASCII. The top bits - attributes +FCB_FT EQU 9 ; Filetype, 7-bit ASCII. + ; T1' to T3' have the following + ; T1' - Read-Only + ; T2' - System (hidden) + ; T3' - Archive +FCB_EXT EQU 12 ; EX - Set this to 0 when opening a file and then leave it to + ; CP/M. You can rewind a file by setting EX, RC, S2 and CR to 0. +FCB_S1 EQU 13 ; S1 - Reserved. +FCB_S2 EQU 14 ; S2 - Reserved. Bit 7 - wile write flag, [6:0] module number (Extent hi bits) +FCB_RC EQU 15 ; RC - Set this to 0 when opening a file and then leave it to CP/M. +FCB_AL EQU 16 ; AL - Image of the second half of the directory entry, + ; containing the file's allocation (which disc blocks it owns). +FCB_CR EQU 32 ; CR - Current record within extent. It is usually best to set + ; this to 0 immediately after a file has been opened and + ; then ignore it. +FCB_RN EQU 33 ; Rn - Random access record number. A 16-bit (with R2 used for overflow) + + +; ------------------------------------------------------ +JP_OPCODE EQU 0xC3 +; ------------------------------------------------------ +IRQ_0 EQU 0x01 +IRQ_1 EQU 0x02 +IRQ_2 EQU 0x04 +;LP_IRQ EQU 0x08 +KBD_IRQ EQU 0x02 + +KBD_ACK EQU 0x10 + +KEY_ALF EQU 0x0D +KEY_FIX EQU 0x15 +; ------------------------------------------------------ +LST_REC EQU 0x7F +; ------------------------------------------------------ +MAX_EXT EQU 0x1F +MAX_MOD EQU 0x0F +;MOD_NUM EQU 0x0E +; ------------------------------------------------------ +FCB_INFO_LEN EQU 15 ; length of FCB info bytes to match +;NXT_REC EQU 0x20 +; ------------------------------------------------------ +PIC_POLL_MODE EQU 0x0A +PORT_C4 EQU 0x10 +PRINTER_ACK EQU 0x10 +PRINTER_IRQ EQU 0x08 +; ------------------------------------------------------ +;RAN_REC EQU 0x21 +;FCB_RC EQU 0x0F +RO_FILE EQU 0x09 +ROM_CHIP_SIZE EQU 8192 ; ROM Size, used to limit monitor code size + +RX_READY EQU 0x02 +; ------------------------------------------------------ +TAPE_D EQU 0x08 +TAPE_P EQU 0x04 +TIMER_IRQ EQU 0x10 +TL_HIGH EQU 0x05 +TL_LOW EQU 0x03 +TL_MID EQU 0x04 +TMR0_SQWAVE EQU 0x36 +TRUE EQU 0xFF +TX_READY EQU 0x01 +; ------------------------------------------------------ + + ENDIF \ No newline at end of file diff --git a/MON_v8_c4eec374/font-6x7.inc b/MON_v8_c4eec374/font-6x7.inc new file mode 100644 index 0000000..221a101 --- /dev/null +++ b/MON_v8_c4eec374/font-6x7.inc @@ -0,0 +1,165 @@ + ; 96 symbols 6x7, with codes 0x20..0x7f KOI-7 H0 +m_font_cp0: + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; ' ' - 0x20 + DB 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04 ; '!' - 0x21 + DB 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00 ; '"' - 0x22 + DB 0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a ; '#' - 0x23 + DB 0x04, 0x1e, 0x05, 0x0e, 0x14, 0x0f, 0x04 ; '$' - 0x24 + DB 0x03, 0x13, 0x08, 0x04, 0x02, 0x19, 0x18 ; '%' - 0x25 + DB 0x06, 0x09, 0x05, 0x02, 0x15, 0x09, 0x16 ; '&' - 0x26 + DB 0x06, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00 ; ' - 0x27 + DB 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08 ; '(' - 0x28 + DB 0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02 ; ')' - 0x29 + DB 0x00, 0x0a, 0x04, 0x1f, 0x04, 0x0a, 0x00 ; '*' - 0x2a + DB 0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00 ; '+' - 0x2b + DB 0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x02 ; ',' - 0x2c + DB 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00 ; '-' - 0x2d + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06 ; '.' - 0x2e + DB 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00 ; '/' - 0x2f + DB 0x0e, 0x11, 0x19, 0x15, 0x13, 0x11, 0x0e ; '0' - 0x30 + DB 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x0e ; '1' - 0x31 + DB 0x0e, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1f ; '2' - 0x32 + DB 0x1f, 0x08, 0x04, 0x08, 0x10, 0x11, 0x0e ; '3' - 0x33 + DB 0x08, 0x0c, 0x0a, 0x09, 0x1f, 0x08, 0x08 ; '4' - 0x34 + DB 0x1f, 0x01, 0x0f, 0x10, 0x10, 0x11, 0x0e ; '5' - 0x35 + DB 0x0c, 0x02, 0x01, 0x0f, 0x11, 0x11, 0x0e ; '6' - 0x36 + DB 0x1f, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02 ; '7' - 0x37 + DB 0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e ; '8' - 0x38 + DB 0x0e, 0x11, 0x11, 0x1e, 0x10, 0x08, 0x06 ; '9' - 0x39 + DB 0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00 ; ':' - 0x3a + DB 0x00, 0x06, 0x06, 0x00, 0x06, 0x04, 0x02 ; ';' - 0x3b + DB 0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08 ; '<' - 0x3c + DB 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00 ; '=' - 0x3d + DB 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02 ; '>' - 0x3e + DB 0x0e, 0x11, 0x10, 0x08, 0x04, 0x00, 0x04 ; '?' - 0x3f + DB 0x0e, 0x11, 0x10, 0x16, 0x15, 0x15, 0x0e ; '@' - 0x40 + DB 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11 ; 'A' - 0x41 + DB 0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f ; 'B' - 0x42 + DB 0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e ; 'C' - 0x43 + DB 0x07, 0x09, 0x11, 0x11, 0x11, 0x09, 0x07 ; 'D' - 0x44 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f ; 'E' - 0x45 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x01 ; 'F' - 0x46 + DB 0x0e, 0x11, 0x01, 0x1d, 0x11, 0x11, 0x1e ; 'G' - 0x47 + DB 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 ; 'H' - 0x48 + DB 0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e ; 'I' - 0x49 + DB 0x1c, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06 ; 'J' - 0x4a + DB 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11 ; 'K' - 0x4b + DB 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1f ; 'L' - 0x4c + DB 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 ; 'M' - 0x4d + DB 0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11 ; 'N' - 0x4e + DB 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; 'O' - 0x4f + DB 0x0f, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x01 ; 'P' - 0x50 + DB 0x0e, 0x11, 0x11, 0x11, 0x15, 0x09, 0x16 ; 'Q' - 0x51 + DB 0x0f, 0x11, 0x11, 0x0f, 0x05, 0x09, 0x11 ; 'R' - 0x52 + DB 0x1e, 0x01, 0x01, 0x0e, 0x10, 0x10, 0x0f ; 'S' - 0x53 + DB 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 'T' - 0x54 + DB 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; 'U' - 0x55 + DB 0x11, 0x11, 0x11, 0x11, 0x0a, 0x0a, 0x04 ; 'V' - 0x56 + DB 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0a ; 'W' - 0x57 + DB 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 ; 'X' - 0x58 + DB 0x11, 0x11, 0x11, 0x0a, 0x04, 0x04, 0x04 ; 'Y' - 0x59 + DB 0x1f, 0x10, 0x08, 0x04, 0x02, 0x01, 0x1f ; 'Z' - 0x5a + DB 0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e ; '[' - 0x5b + DB 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00 ; '\' - 0x5c + DB 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e ; ']' - 0x5d + DB 0x0e, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00 ; '^' - 0x5r + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f ; '_' - 0x5f + DB 0x1c, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00 ; '`' - 0x60 + DB 0x00, 0x00, 0x0e, 0x10, 0x1e, 0x13, 0x1e ; 'a' - 0x61 + DB 0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x0f ; 'b' - 0x62 + DB 0x00, 0x00, 0x0e, 0x01, 0x01, 0x01, 0x0e ; 'c' - 0x63 + DB 0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1e ; 'd' - 0x64 + DB 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x0e ; 'e' - 0x65 + DB 0x18, 0x04, 0x04, 0x0e, 0x04, 0x04, 0x04 ; 'f' - 0x66 + DB 0x00, 0x0e, 0x11, 0x11, 0x1e, 0x10, 0x0e ; 'g' - 0x67 + DB 0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x11 ; 'h' - 0x68 + DB 0x04, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04 ; 'i' - 0x69 + DB 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x06 ; 'j' - 0x6a + DB 0x01, 0x01, 0x09, 0x05, 0x03, 0x05, 0x09 ; 'k' - 0x6b + DB 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08 ; 'l' - 0x6c + DB 0x00, 0x00, 0x0f, 0x15, 0x15, 0x15, 0x15 ; 'm' - 0x6d + DB 0x00, 0x00, 0x09, 0x13, 0x11, 0x11, 0x11 ; 'n' - 0x6e + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e ; 'o' - 0x6f + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x0f, 0x01 ; 'p' - 0x70 + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x1e, 0x10 ; 'q' - 0x71 + DB 0x00, 0x00, 0x0d, 0x13, 0x01, 0x01, 0x01 ; 'r' - 0x72 + DB 0x00, 0x00, 0x1e, 0x01, 0x0e, 0x10, 0x0f ; 's' - 0x73 + DB 0x04, 0x04, 0x0e, 0x04, 0x04, 0x04, 0x18 ; 't' - 0x74 + DB 0x00, 0x00, 0x11, 0x11, 0x11, 0x19, 0x16 ; 'u' - 0x75 + DB 0x00, 0x00, 0x11, 0x11, 0x0a, 0x0a, 0x04 ; 'v' - 0x76 + DB 0x00, 0x00, 0x11, 0x15, 0x15, 0x15, 0x0a ; 'w' - 0x77 + DB 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11 ; 'x' - 0x78 + DB 0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0c ; 'y' - 0x79 + DB 0x00, 0x00, 0x1f, 0x08, 0x04, 0x02, 0x1f ; 'z' - 0x7a + DB 0x0c, 0x02, 0x02, 0x01, 0x02, 0x02, 0x0c ; '{' - 0x7b + DB 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; '|' - 0x7c + DB 0x03, 0x04, 0x04, 0x08, 0x04, 0x04, 0x03 ; '}' - 0x7d + DB 0x00, 0x02, 0x15, 0x0a, 0x15, 0x08, 0x00 ; '~' - 0x7e + DB 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15 ; [DEL] - 0x7f + +; 64 symbols 6x7, with codes 0x40..0x7f KOI-7 H1 +m_font_cp1: + DB 0x00, 0x00, 0x09, 0x15, 0x17, 0x15, 0x09 ; ю - 0x40 + DB 0x00, 0x00, 0x06, 0x08, 0x0e, 0x09, 0x16 ; а - 0x41 + DB 0x07, 0x02, 0x04, 0x0e, 0x09, 0x09, 0x06 ; б - 0x42 DIFF + DB 0x00, 0x00, 0x09, 0x09, 0x09, 0x1f, 0x10 ; ц - 0x43 + DB 0x03, 0x04, 0x08, 0x0e, 0x09, 0x09, 0x06 ; д - 0x44 DIFF + DB 0x00, 0x00, 0x0e, 0x11, 0x1f, 0x01, 0x1e ; е - 0x45 + DB 0x00, 0x04, 0x0e, 0x15, 0x15, 0x0e, 0x04 ; ф - 0x46 + DB 0x00, 0x00, 0x0f, 0x09, 0x01, 0x01, 0x01 ; г - 0x47 + DB 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11 ; х - 0x48 + DB 0x00, 0x00, 0x11, 0x19, 0x15, 0x13, 0x11 ; и - 0x49 + DB 0x0a, 0x04, 0x11, 0x19, 0x15, 0x13, 0x11 ; й - 0x4a + DB 0x00, 0x00, 0x11, 0x09, 0x07, 0x09, 0x11 ; к - 0x4b + DB 0x00, 0x00, 0x1c, 0x12, 0x12, 0x12, 0x11 ; л - 0x4c + DB 0x00, 0x00, 0x11, 0x1b, 0x15, 0x11, 0x11 ; м - 0x4d + DB 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11 ; н - 0x4e + DB 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e ; о - 0x4f + DB 0x00, 0x00, 0x1f, 0x11, 0x11, 0x11, 0x11 ; п - 0x50 + DB 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x14, 0x12 ; я - 0x51 + DB 0x00, 0x00, 0x07, 0x09, 0x07, 0x01, 0x01 ; р - 0x52 + DB 0x00, 0x00, 0x0e, 0x01, 0x01, 0x01, 0x0e ; с - 0x53 + DB 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04 ; т - 0x54 + DB 0x00, 0x00, 0x11, 0x11, 0x1e, 0x10, 0x0e ; у - 0x55 + DB 0x00, 0x00, 0x15, 0x15, 0x0e, 0x15, 0x15 ; ж - 0x56 + DB 0x00, 0x00, 0x03, 0x05, 0x07, 0x09, 0x07 ; в - 0x57 + DB 0x00, 0x00, 0x01, 0x01, 0x07, 0x09, 0x07 ; ь - 0x58 + DB 0x00, 0x00, 0x11, 0x11, 0x13, 0x15, 0x13 ; ы - 0x59 + DB 0x00, 0x00, 0x0e, 0x11, 0x0c, 0x11, 0x0e ; з - 0x5a + DB 0x00, 0x00, 0x15, 0x15, 0x15, 0x15, 0x1f ; ш - 0x5b + DB 0x00, 0x00, 0x07, 0x08, 0x0e, 0x08, 0x07 ; э - 0x5c + DB 0x00, 0x00, 0x15, 0x15, 0x15, 0x1f, 0x10 ; щ - 0x5d + DB 0x00, 0x00, 0x09, 0x09, 0x0e, 0x08, 0x08 ; ч - 0x5e + DB 0x00, 0x00, 0x06, 0x05, 0x0c, 0x14, 0x0c ; ъ - 0x5f + DB 0x09, 0x15, 0x15, 0x17, 0x15, 0x15, 0x09 ; Ю - 0x60 + DB 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11 ; А - 0x61 + DB 0x1f, 0x11, 0x01, 0x0f, 0x11, 0x11, 0x1f ; Б - 0x62 + DB 0x09, 0x09, 0x09, 0x09, 0x09, 0x1f, 0x10 ; С - 0x63 + DB 0x0c, 0x0a, 0x0a, 0x0a, 0x0a, 0x1f, 0x11 ; Д - 0x64 + DB 0x1f, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1f ; Е - 0x65 + DB 0x04, 0x0e, 0x15, 0x15, 0x15, 0x0e, 0x04 ; Ф - 0x66 + DB 0x1f, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01 ; Г - 0x67 + DB 0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11 ; Х - 0x68 + DB 0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11 ; И - 0x69 + DB 0x04, 0x15, 0x11, 0x19, 0x15, 0x13, 0x11 ; Й - 0x6a + DB 0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11 ; К - 0x6b + DB 0x1c, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11 ; Л - 0x6c + DB 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11 ; М - 0x6d + DB 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11 ; Н - 0x6e + DB 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e ; О - 0x6f + DB 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 ; П - 0x70 + DB 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x12, 0x11 ; Я - 0x71 + DB 0x0f, 0x11, 0x11, 0x11, 0x0f, 0x01, 0x01 ; Р - 0x72 + DB 0x0e, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0e ; С - 0x73 + DB 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; Т - 0x74 + DB 0x11, 0x11, 0x11, 0x11, 0x1e, 0x10, 0x0e ; У - 0x75 + DB 0x15, 0x15, 0x15, 0x0e, 0x15, 0x15, 0x15 ; Ж - 0x76 + DB 0x0f, 0x11, 0x11, 0x0f, 0x11, 0x11, 0x0f ; В - 0x77 + DB 0x01, 0x01, 0x01, 0x0f, 0x11, 0x11, 0x0f ; Ь - 0x78 + DB 0x11, 0x11, 0x11, 0x13, 0x15, 0x15, 0x13 ; Ы - 0x79 + DB 0x0e, 0x11, 0x10, 0x0c, 0x10, 0x11, 0x0e ; З - 0x7a + DB 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f ; Ш - 0x7b + DB 0x0e, 0x11, 0x10, 0x1c, 0x10, 0x11, 0x0e ; Э - 0x7c + DB 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f, 0x10 ; Щ - 0x7d + DB 0x11, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10 ; Ч - 0x7e + DB 0x1f, 0x15, 0x1f, 0x15, 0x1f, 0x15, 0x1f ; [DEL] - 0x7f diff --git a/MON_v8_c4eec374/io.inc b/MON_v8_c4eec374/io.inc new file mode 100644 index 0000000..d884e0b --- /dev/null +++ b/MON_v8_c4eec374/io.inc @@ -0,0 +1,132 @@ +; ======================================================= +; Ocean-240.2 +; Computer with FDC variant. +; IO Ports definitions +; +; By Romych 2025-09-09 +; ======================================================= + + IFNDEF _IO_PORTS + DEFINE _IO_PORTS + +; ------------------------------------------------------- +; КР580ВВ55 DD79 +; ------------------------------------------------------- +; Port A - User port A +USR_DD79PA EQU 0x00 + +; Port B - User port B +USR_DD79PB EQU 0x01 + +; Port C - User port C +USR_DD79PC EQU 0x02 + +; Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1] +USR_DD79CTR EQU 0x03 + +; ------------------------------------------------------- +; КР1818ВГ93 +; ------------------------------------------------------- +; CMD +FDC_CMD EQU 0x20 + +; TRACK +FDC_TRACK EQU 0x21 + +; SECTOR +FDC_SECT EQU 0x22 + +; DATA +FDC_DATA EQU 0x23 + +; +FDC_WAIT EQU 0x24 + +; Controller port +FLOPPY EQU 0x25 + + +; ------------------------------------------------------- +; КР580ВВ55 DD78 +; ------------------------------------------------------- +; Port A - Keyboard Data +KBD_DD78PA EQU 0x40 + +; Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +KBD_DD78PB EQU 0x41 + +; Port C - [PC7..0] +KBD_DD78PC EQU 0x42 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +KBD_DD78CTR EQU 0x43 + + +; ------------------------------------------------------- +; КР580ВИ53 DD70 +; ------------------------------------------------------- +; Counter 1 +TMR_DD70C1 EQU 0x60 + +; Counter 2 +TMR_DD70C2 EQU 0x61 + +; Counter 3 +TMR_DD70C3 EQU 0x62 + +; Config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] +; sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB +; mode 000 - int on fin, +; 001 - one shot, +; x10 - rate gen, +; x11-sq wave +TMR_DD70CTR EQU 0x63 + +; Programable Interrupt controller PIC KR580VV59 +PIC_DD75RS EQU 0x80 +PIC_DD75RM EQU 0x81 + +; ------------------------------------------------------- +; КР580ВВ51 DD72 +; ------------------------------------------------------- +; Data +UART_DD72RD EQU 0xA0 + +; [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +UART_DD72RR EQU 0xA1 + +; ------------------------------------------------------- +; КР580ВВ55 DD17 +; ------------------------------------------------------- +; Port A - VShift[8..1] +SYS_DD17PA EQU 0xC0 + +; Port B - [ROM14,13][REST][ENROM-][A18,17,16][32k] +SYS_DD17PB EQU 0xC1 + +; Port C - HShift[HS5..1,SB3..1] +SYS_DD17PC EQU 0xC2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +SYS_DD17CTR EQU 0xC3 + +; ------------------------------------------------------- +; КР580ВВ55 DD67 +; ------------------------------------------------------- +; Port A - LPT Data +LPT_DD67PA EQU 0xE0 + +; Port B - [VSU,C/M,FL3..1,COL3..1] +VID_DD67PB EQU 0xE1 + +; Port C - [USER3..1,STB-LP,BELL,TAPE3..1] +DD67PC EQU 0xE2 + +; Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +; Set bit: [0][xxx][bbb][0|1]; +DD67CTR EQU 0xE3 + + ENDIF \ No newline at end of file diff --git a/MON_v8_c4eec374/m_vars.inc b/MON_v8_c4eec374/m_vars.inc new file mode 100644 index 0000000..578bddd --- /dev/null +++ b/MON_v8_c4eec374/m_vars.inc @@ -0,0 +1,101 @@ +; ======================================================= +; Ocean-240.2 +; Module M_VARS - Monitor variables +; RAM Range: 0xBA09-0xBFFF +; +; Disassembled by Romych 2025-02-05 +; ======================================================= + + IFNDEF _M_VARS + DEFINE _M_VARS + + MODULE M_VARS + ORG 0xbf00 + +buffer DS 128 ; 0xbf00 search text or buffer to search? + DS 36 +paint_stack EQU $ ; 0xbfa4 + DS 32 +stack1: EQU $ ; 0xbfc4 + DS 1 +paint_var1 DS 1 ; 0xbfc5 +paint_var2 DS 1 ; 0xbfc6 +paint_var3 DS 1 ; 0xbfc7 +paint_var4 DS 1 ; 0xbfc8 +paint_var5 DS 1 ; 0xbfc9 +paint_y DS 1 ; 0xbfca +paint_var7 DS 1 ; 0xbfcb +cmp_color DS 1 ; 0xbfcc +paint_sp_save DS 2 + +; Right pixel mask ex: 11111000 +pixel_mask_r DS 1 ; 0xbfcf +tmp_color DS 1 ; 0xbfd0 +rect_var2 DS 1 ; 0xbfd1 +stack_0 EQU $ +rect_var3 DS 1 ; 0xbfd2 +esc_mode DS 1 ; 0xbfd3 +esc_cmd DS 1 ; 0xbfd4 + +esc_param_cnt DS 1 ; 0xbfd5 +esc_param DS 7 ; 0xbfd6 + +; Left inverse pixel mask ex: 00011111 +pixel_mask_l_i DS 1 +; Right inverse pixel mask ex: 00011111 +pixel_mask_r_i DS 1 +; Left pixel mask ex: 11100000 +pixel_mask_l DS 1 + +; Current screen mode, bit 3 - hide/show cursor, bit 4 - only 20 rows +screen_mode DS 1 ; 0xbfe0 +cursor_row DS 1 ; 0xbfe1 Cursor Y position +cursor_col DS 1 ; 0xbfe2 Cursor X position +curr_color DS 2 ; 0xbfe3 Current color low and hi bytes +esc_hex_cmd: +row_shift DS 1 ; 0xbfe5 +codepage DS 1 ; 0xbfe6 + + +cur_palette DS 1 ; 0xbfe7 00bbbfff - background and foreground colors +beep_period DS 2 ; 0xbfe8 +beep_duration DS 2 ; 0xbfea +pix_shift DS 1 ; 0xbfec +strobe_state DS 1 ; 0xbfed +ul_var0 DS 1 ; 0xbfee +ul_A_var1 DS 1 ; 0xbfef +ul_B_var2 DS 1 ; 0xbff0 +ul_var3 DS 1 ; 0xbff1 +ul_A_var4 DS 1 ; 0xbff2 +ul_B_var5 DS 1 ; 0xbff3 +ul_var6 DS 1 ; 0xbff4 +esc_var0 DS 1 ; 0xbff5 +esc_var1 DS 1 ; 0xbff6 +esc_var2 DS 1 ; 0xbff7 +esc_var3 DS 1 ; 0xbff8 + DS 1 ; 0xbff9 + DS 1 ; 0xbffa + DS 1 ; 0xbffb + DS 1 ; 0xbffc + DS 1 ; 0xbffd + DS 1 ; 0xbffe + DS 1 ; 0xbfff + + ASSERT stack1 = 0xbfc4 + ASSERT buffer = 0xbf00 + ASSERT paint_var1 = 0xbfc5 + ASSERT pixel_mask_r = 0xbfcf + ASSERT stack_0 = 0xbfd2 + ASSERT esc_mode = 0xbfd3 + ASSERT screen_mode = 0xbfe0 + ASSERT cur_palette = 0xbfe7 + ASSERT ul_var0 = 0xbfee + ASSERT paint_stack = 0xbfa4 + ASSERT esc_var3 = 0xbff8 + + ;DISPLAY "screen_mode: ", /H, screen_mode + ;DISPLAY "fn48_var1: ", /H, fn48_var1 + + ENDMODULE + + ENDIF diff --git a/MON_v8_c4eec374/monitor.asm b/MON_v8_c4eec374/monitor.asm new file mode 100644 index 0000000..8960185 --- /dev/null +++ b/MON_v8_c4eec374/monitor.asm @@ -0,0 +1,5593 @@ +; ====================================================== +; Ocean-240.2 +; Monitor V8.8 +; +; Disassembled by Romych 2026-02-05 +; ====================================================== + + DEVICE NOSLOT64K + + INCLUDE "io.inc" + INCLUDE "equates.inc" + INCLUDE "ram.inc" + INCLUDE "bios_entries.inc" + + OUTPUT monitor_E000.bin + + + MODULE MONITOR + + ORG 0xE000 + +; ------------------------------------------------------ +; Monitor Entry points +; ------------------------------------------------------ + +start: JP m_hot_start ; E000 +mon_hexb: JP m_hexb ; E003 +non_con_status: JP m_con_status ; E006 +mon_con_in: JP m_con_in ; E009 +mon_con_out: JP m_con_out ; E00C +mon_serial_in: JP m_serial_in ; E00F +mon_serial_out: JP m_serial_out ; E012 +mon_char_print: JP m_char_print ; E015 +mon_tape_read: JP m_tape_read ; E018 +mon_tape_write: JP m_tape_write ; E01B +ram_disk_read: JP m_ramdisk_read ; E01E +ram_disk_write: JP m_ramdisk_write ; E021 +mon_tape_read_ram: JP m_tape_read_ram2 ; E024 +mon_tape_write_ram: JP m_tape_write_ram2 ; E027 +mon_tape_wait: JP m_tape_wait ; E02A +mon_tape_detect: JP m_tape_blk_detect ; E02D +read_floppy: JP m_read_floppy ; E030 +write_floppy: JP m_write_floppy ; E033 +mon_out_str_z: JP m_out_strz ; E036 + JP m_fn_39 ; E039 + JP get_image_hdr ; E03C + JP esc_picture ; E03F + JP m_print_at_xy ; E042 C-char esc_param[0]=X, [1]=Y + JP esc_draw_fill_rect ; E045 + JP esc_paint ; E048 + JP esc_draw_line ; E04B + JP esc_draw_circle ; E04E + + +; ------------------------------------------------------ +; Init system devices +; ------------------------------------------------------ +m_hot_start: + DI + ;LD SP, M_VARS.rst_ret_jp + LD A, 10000000b ; DD17 all ports to out + OUT (SYS_DD17CTR), A ; VV55 Sys CTR + OUT (DD67CTR), A ; VV55 Video CTR + + ; init_kbd_tape + LD A, 0x93 + OUT (KBD_DD78CTR), A + + + LD A, 01111111b ; VSU=0, C/M=1, FL=111, COL=111 + OUT (VID_DD67PB), A ; color mode + LD A, 00000001b + OUT (SYS_DD17PB), A ; Access to VRAM + LD B, 0x0 ; TODO: replace to LD HL, 0x3f00 LD B,L + LD HL, 0x3f00 + LD A, H + ADD A, 0x41 ; A=128 0x80 + + ; Clear memory from 0x3F00 to 0x7FFF +.fill_video: + LD (HL), B + INC HL + CP H + JP NZ, .fill_video + + ;XOR A + LD A, 0 + OUT (SYS_DD17PB), A ; Disable VRAM + LD A, 00000111b + OUT (SYS_DD17PC), A ; pix shift to 7 + LD (M_VARS.pix_shift), A + + XOR A + LD (M_VARS.screen_mode), A + LD (M_VARS.row_shift), A + + ; Set color mode and palette + LD (M_VARS.curr_color+1), A + CPL + LD (M_VARS.curr_color), A + LD A, 00000011b + LD (M_VARS.cur_palette), A + ; VSU=0, C/M=1, FL=000, COL=011 + ; color mode, black border + ; 00-black, 01-red, 10-purple, 11-white + LD A, 01000011b + OUT (VID_DD67PB), A + + ; config LPT + LD A, 0x4 + OUT (DD67PC), A ; bell=1, strobe=0 + LD (M_VARS.strobe_state), A ; store strobe + LD HL, 1024 ; 683us + LD (M_VARS.beep_period), HL + LD HL, 320 ; 213us + LD (M_VARS.beep_duration), HL + +.conf_uart: + ; Config UART + LD A, 11001110b + OUT (UART_DD72RR), A + LD A, 00100101b + OUT (UART_DD72RR), A + + ; Config Timer#1 for UART clock + LD A, 01110110b ; tmr#1, load l+m bin, sq wave + OUT (TMR_DD70CTR), A + + ; 1.5M/20 = 75kHz + LD A, 20 + OUT (TMR_DD70C2), A + XOR A + OUT (TMR_DD70C2), A +.conf_pic: + ; Config PIC + LD A,00010010b ; ICW1 edge trigger, interval 8, sin... + OUT (PIC_DD75RS), A + XOR A + OUT (PIC_DD75RM), A ; ICW2 + CPL + OUT (PIC_DD75RM), A ; ICW3 no slave + LD A,00100000b + OUT (PIC_DD75RS), A ; Non-specific EOI command, End of I... + LD A, PIC_POLL_MODE + OUT (PIC_DD75RS), A ; Poll mode, poll on next RD + + LD A, 0x80 + OUT (KBD_DD78PC), A ; TODO: - Check using this 7th bit + NOP + NOP + XOR A + OUT (KBD_DD78PC), A + + ; Init cursor + LD SP, M_VARS.stack1 + CALL m_draw_cursor + + ; Beep + LD C, ASCII_BELL + CALL m_con_out + + LD A, (BIOS.boot_f) + CP JP_OPCODE + JP Z, BIOS.boot_f + LD HL, mgs_system_nf + CALL m_out_strz + JP m_sys_halt + +; -------------------------------------------------- +; Output ASCIIZ string +; Inp: HL -> string +; -------------------------------------------------- +m_out_strz: + LD C, (HL) + LD A, C + OR A + RET Z + CALL m_con_out + INC HL + JP m_out_strz + +mgs_system_nf: + DB "\r\nSYSTEM NOT FOUND\r\n", 0 + +m_sys_halt: + HALT + +; ------------------------------------------------------ +; Console status +; Out: A = 0 - not ready +; A = 0xFF - ready (key pressed) +; ------------------------------------------------------ +m_con_status: + IN A, (PIC_DD75RS) ; Read PIC status + NOP + AND KBD_IRQ ; Check keyboard request RST1 + LD A, 0 + RET Z ; no key pressed + CPL + RET ; key pressed + +; ------------------------------------------------------ +; Wait and read data from UART +; Out: A - 7 bit data +; ------------------------------------------------------ +m_serial_in: + IN A, (UART_DD72RR) + AND RX_READY + JP Z, m_serial_in ; wait for rx data ready + IN A, (UART_DD72RD) + AND 0x7f ; leave 7 bits + RET + +; ------------------------------------------------------ +; Read key +; Out: A +; ------------------------------------------------------ +m_con_in: + CALL m_con_status + OR A + JP Z, m_con_in ; wait key + IN A, (KBD_DD78PA) ; get key + AND 0x7f ; reset hi bit, leave 0..127 code + PUSH AF + ; TODO: Check if it is keyboard ACK + ; PC7 Set Hi (ACK?) + LD A, 0x80 + OUT (KBD_DD78PC), A + ; PC7 Set Lo + XOR A + OUT (KBD_DD78PC), A + POP AF + RET + +; ------------------------------------------------------ +; Send data by UART +; Inp: C - data to transmitt +; ------------------------------------------------------ +m_serial_out: + IN A, (UART_DD72RR) + AND TX_READY + JP Z, m_serial_out ; Wait for TX ready + LD A, C + OUT (UART_DD72RD), A + RET + +; ------------------------------------------------------ +; Send character to printer +; Inp: C - character +; ------------------------------------------------------ +m_char_print: + ; wait printer ready + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP Z, m_char_print + + LD A, C + NOP + OUT (LPT_DD67PA), A + ; set LP strobe + LD A, 00010100b + OUT (DD67PC),A + +.wait_lp: + ; wait printer ack + IN A, (PIC_DD75RS) + AND PRINTER_IRQ + JP NZ, .wait_lp + ; remove LP strobe + LD A, 00000100b + OUT (DD67PC), A + RET + +; ------------------------------------------------------ +; Out char to console +; Inp: C - char +; ------------------------------------------------------ +m_con_out: + PUSH HL + PUSH DE + PUSH BC + CALL m_con_out_int + POP BC + POP DE + POP HL + RET + +; ------------------------------------------------------ +; Out char C to console +; ------------------------------------------------------ +m_con_out_int: + LD DE, M_VARS.esc_mode + LD A, (DE) + DEC A + OR A ; TODO: unused (save 1b 4t) + JP M, m_print_no_esc ; esc_mode=0 - standart print no ESC mode + JP NZ, m_print_at_xy ; esc_mode=2 (graphics) + + ; handle ESC param (esc_mode=1) + INC DE ; TODO: replace to INC E E=0xd3 save 2t + LD A, (DE) + OR A + JP P, get_esc_param + LD A, C + AND 0xf ; convert char to command code + LD (DE), A + INC DE ; TODO: replace to INC E E=0xd3 save 2t + XOR A + LD (DE), A + RET + +get_esc_param: + LD HL, M_VARS.esc_cmd + LD B, (HL) ; TODO: replace to INC L L=0xd4 save 2t + INC HL ; HL -> param count + LD A, (HL) + INC A + LD (HL), A + ; store new param + LD E, A + LD D, 0x0 + ADD HL, DE ; HL -> parameter[param_count] + LD (HL), C ; store letter as esc parameter + ; get params count for esc command + LD HL, esc_params_tab + LD E, B ; d=0, b = cmd + ADD HL, DE ; DE - command offset + CP (HL) + ; return if enough + RET M + +;esc_set_mode: + LD HL, M_VARS.esc_cmd + LD A, (HL) + AND 0x0f ; mask (cmd=0..15) + LD E, A + DEC HL ; HL -> esc_mode + OR A + LD (HL), 2 ; mode=2 for cmd=0 + RET Z ; just return, no handler there + + LD D, 0 ; TODO: remove, D already 0 + LD (HL), D ; reset mode to 0 for other + DEC DE ; DE = cmd-1 + +;co_get_hdlr: + ; Calc ESC command handler offset + LD HL, esc_handler_tab + ADD HL, DE + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) + ; HL = addr of handler func + EX DE, HL + ; It is 1..4 func DRAW_* func? + CP 0x4 + JP P, esc_no_draw_fn + LD A, (M_VARS.screen_mode) + AND 00000011b + ; If not in graphics mode - exit + JP NZ, esc_exit + +esc_no_draw_fn: + LD DE, esc_exit + PUSH DE + + ; Jump to ESC func handler + JP (HL) + +esc_exit: + XOR A + LD (M_VARS.esc_mode), A + RET + + ; Count of parameters for ESC commands + ; 0xe1cb +esc_params_tab: + DB 3, 5, 4, 3, 1, 2, 1, 1 + DB 1, 2, 1, 5, 5, 7, 6, 4 + +esc_handler_tab: + DW esc_draw_fill_rect ;5 1x1y1x2y2m + DW esc_draw_line ;4 2x1y1x2y2 + DW esc_draw_dot ;3 3xxyy + DW esc_set_color ;1 4N N=1..4 + DW esc_set_cursor ;2 5rc r-Row, c-Col + DW esc_set_vmode ;1 6m m-mode: + ; C 0 - 40x25 cursor on + ; M 1,2 - 64x25 cursor on + ; M 3 - 80x25 cursor on + ; C 4 - 40x25 cursor off + ; M 5,6 - 64x25 cursor off + ; M 7 - 80x25 cursor off + ; M 8 - 20rows mode + ; 9 - cursor off + ; 10 - cursor on + DW esc_set_charset ;1 7n where n is: + ; 0 - LAT Both cases + ; 1 - RUS Both cases + ; 2 - LAT+RUS Upper case + DW esc_set_palette ;1 8c c - Foreground+Backgound + DW esc_set_cursor2 ;2 9xy + DW esc_print_screen ;1 : + DW esc_draw_circle ;5 ;xyraxay X,Y, Radius, aspect ratio X, aspect ratio Y + DW esc_paint ;5 = + DW esc_picture ;6 > + DW esc_set_beep ;4 ?ppdd pp-period (word), dd - duration (word) + +esc_set_beep: + ; param byte 1+2 -> period + LD DE, M_VARS.esc_param + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (M_VARS.beep_period), HL + ; param byte 3+4 -> duration + INC DE + LD A, (DE) + LD H, A + INC DE + LD A, (DE) + LD L, A + LD (M_VARS.beep_duration), HL + RET + +esc_set_cursor2: + JP esc_set_cursor + +esc_print_screen: + LD A, (M_VARS.screen_mode) + AND 00000011b + RET NZ ; ret if not 0-3 mode + LD DE, 0x30ff + CALL m_print_hor_line + DEC E + LD D, 0xf0 + +.chk_keys: + CALL m_con_status + OR A + JP Z, .no_keys + CALL m_con_in + CP ASCII_ESC + RET Z + +.no_keys: + CALL m_print_hor_line + DEC E + JP NZ, .chk_keys + LD D, 0xe0 ; 224d + CALL m_print_hor_line + RET + +; ------------------------------------------------------ +; Print line to printer +; D - width +; ------------------------------------------------------ +m_print_hor_line: + LD HL, cmd_esc_set_X0 + + ; Set printer X coordinate = 0 + CALL m_print_cmd + LD HL, 4 + LD (M_VARS.ul_var0), HL ; Set start coord X = 4 + LD B, 0x0 ; TODO: LD B, H (save 1b 3t) + +.print_next_col: + LD C, 0x0 + ; 1 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (M_VARS.ul_var0) + INC HL + + ; inc X + LD (M_VARS.ul_var0), HL + LD C, 0x1 + ; 2 + CALL m_get_7vpix + AND D + CALL NZ, m_print_vert_7pix + LD HL, (M_VARS.ul_var0) + INC HL + ; inc X + LD (M_VARS.ul_var0), HL + INC B + LD A, B + CP 236 + JP C, .print_next_col + LD HL, cmd_esc_inc_Y2 + CALL m_print_cmd + RET + +; ------------------------------------------------------ +; Send command to printer +; Inp: HL -> command bytes array +; ------------------------------------------------------ +m_print_cmd: + PUSH BC +.print_nxt: + LD A, (HL) + CP ESC_CMD_END + JP Z, .cmd_end + LD C, A + CALL m_char_print + INC HL + JP .print_nxt +.cmd_end: + POP BC + RET + +; ------------------------------------------------------ +; Print 7 vertical pixels to printer +; Inp: A - value to print +; ------------------------------------------------------ +m_print_vert_7pix: + PUSH AF + ; Set coordinate X to 0 + LD HL, cmd_esc_set_X + CALL m_print_cmd + LD HL, (M_VARS.ul_var0) + LD C,H + CALL m_char_print + LD C,L + CALL m_char_print + ; Set column print mode + LD HL, cmd_esc_print_col + CALL m_print_cmd + POP AF + ; Print 7 vertical pixels + LD C, A + CALL m_char_print + RET + +; ------------------------------------------------------ +; Control codes for printer УВВПЧ-30-004 +; ------------------------------------------------------ +; Zn - Increment Y coordinate +; 0xe2a5 +cmd_esc_inc_Y2: + DB ASCII_ESC + DB 'Z' + DB 2h + DB ESC_CMD_END + +; Xnn - Set X coordinate +cmd_esc_set_X0: + DB ASCII_ESC + DB 'X' + DB 0h ; 0..479 + DB 0h + DB ESC_CMD_END + +; ------------------------------------------------------ +; X - Start on "Set X coordinate" command +; ------------------------------------------------------ +cmd_esc_set_X: + DB ASCII_ESC + DB 'X' + DB ESC_CMD_END + +; O - Column print (vertical 7 bit) +cmd_esc_print_col: + DB ASCII_ESC + DB 'O' + DB ESC_CMD_END + +; ------------------------------------------------------ +; Get 7 vertical pixels from screen +; Inp: C - sheet +; Out: A - byte +; ------------------------------------------------------ +m_get_7vpix: + LD A, (M_VARS.row_shift) + ADD A, B + ADD A, 19 ; skip first 20pix + LD L, A + PUSH DE + PUSH BC + LD A, E + +.calc_pix_no: + AND 0x7 + LD B, A + LD A, E + ; calc hi addr + RRA ; /8 + RRA + RRA + AND 0x1f + ADD A, A ; *2 + ADD A, 64 ; bytes per row + LD H, A + ; select sheet 0|1 + LD A, C + AND 0x1 + ADD A, H + LD H, A + ; HL = pix addr, turn on VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD E, (HL) ; read pixel + INC H ; HL += 512 + INC H + LD D, (HL) ; read pixel row+1 + + ; turn off VRAM access + ;v8 XOR A + LD A, 0 + OUT (SYS_DD17PB), A +.for_all_pix: + DEC B + JP M, .all_shifted + ; shift pixels D >> [CF] >> E + LD A, D + RRA + LD D, A + LD A, E + RRA + LD E, A + JP .for_all_pix +.all_shifted: + LD A, E + LD D, 0 + RRA + JP NC,.not_1_1 + LD D,00110000b +.not_1_1: + RRA + JP NC, .not_1_2 + LD A, D + OR 11000000b + LD D, A +.not_1_2: + LD A, D + POP BC + POP DE + RET + +esc_set_palette: + LD A, (M_VARS.esc_param) + AND 00111111b ; bgcol[2,1,0],pal[2,1,0] + LD (M_VARS.cur_palette), A + LD B, A + LD A, (M_VARS.screen_mode) + AND 00000011b + LD A, 0x0 + JP NZ, esp_no_colr + LD A, 0x40 + +esp_no_colr: + OR B + OUT (VID_DD67PB), A + RET + +esc_set_charset: + LD A, (M_VARS.esc_param) + AND 0x3 ; charset 0..3 + LD (M_VARS.codepage), A + RET + +; ------------------------------------------------------ +; Get address for draw symbol glyph +; Inp: A - ascii code +; Out: HL -> glyph offset +; ------------------------------------------------------ +m_get_glyph: + LD L, A ; L = ascii code + LD E, A ; E = ascii code + XOR A + LD D, A + LD H, A + ; HL = DE = ascii code + ADD HL, HL + ADD HL, DE + ADD HL, HL + ADD HL, DE + ; HL = A * 7 + LD A, E ; A = A at proc entry + CP '@' + ; First 64 symbols is same for all codepages + JP M, .cp_common + LD A, (M_VARS.codepage) + OR A + ; cp=0 - Latin letters + JP Z, .cp_common + DEC A + ; cp=1 - Russian letters + JP Z, .cp_rus + ; cp=2 - 0x40..0x5F - displayed as Lat + ; 0x60 - 0x7F - displayed as Rus + LD A, E + CP 0x60 + JP M, .cp_common +.cp_rus: + LD DE, 448 ; +448=64*7 Offset for cp1 + ADD HL, DE + +.cp_common: + LD DE, m_font_cp0-224 ; m_font_cp0-32*7 + ADD HL, DE ; add symbol glyph offset + RET + + +; -------------------------------------------------- +; Console output +; Inp: C - char +; -------------------------------------------------- +m_print_no_esc: + LD A, C + AND 0x7f ; C = 0..127 ASCII code + CP ASCII_SP ; C < ' '? + JP M, m_handle_esc_code ; jump if less + CALL m_get_glyph + EX DE, HL + LD A, (M_VARS.screen_mode) + AND 0x3 + JP NZ, mp_mode_64 ; jump to non color modes + + CALL calc_addr_40 + INC L + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + DEC H + DEC H + ; one or two bytes + LD A, B + OR B + JP Z, .l1 + DEC B + JP Z, .l2 + DEC B + JP Z, .l3 + JP .l4 +.l1: + INC H + INC H + LD BC, 0xffc0 + LD A, 0x0 + JP .l5 +.l2: + LD BC, 0xf03f + LD A, 0x6 + JP .l5 +.l3: + LD BC, 0xfc0f + LD A, 0x4 + JP .l5 +.l4: + LD BC, 0xff03 + LD A, 0x2 +.l5: + LD (M_VARS.esc_var1), A + EX DE, HL + +.sym_draw: + LD A, (M_VARS.esc_var1) + PUSH HL + LD L, (HL) + LD H, 0x0 + OR A + JP Z, .pne_l8 + +.pne_l7: + ADD HL, HL + DEC A + JP NZ, .pne_l7 + +.pne_l8: + EX DE, HL + LD A, (HL) + AND C + LD (HL), A + LD A, (M_VARS.curr_color) + AND E + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND C + LD (HL), A + LD A, (M_VARS.curr_color+1) + AND E + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, (M_VARS.curr_color) + AND D + OR (HL) + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, (M_VARS.curr_color+1) + AND D + OR (HL) + LD (HL), A + INC L + DEC H + DEC H + DEC H + EX DE, HL + POP HL + INC HL + LD A, (M_VARS.esc_var0) + DEC A + LD (M_VARS.esc_var0), A + JP NZ, .sym_draw + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + ; draw cursor on return + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_rt: + INC HL + LD A, (HL) ; a = col + ADD A, 1 ; col+1 + AND 0x3f ; screen column 0..63 + LD (HL), A ; save new col + CP 40 + DEC HL + RET M ; Return if no wrap + +m40_wrap_rt: + INC HL + XOR A + LD (HL), A + DEC HL + LD A, (M_VARS.screen_mode) + AND 0x08 ; screen_mode=8? + JP NZ, m2_lf + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_bksp: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A + AND 0x3f ; A=0..63 + CP 0x3f + JP Z, .wrap + LD (HL), A + DEC HL + RET + +.wrap: + LD A, 39 + LD (HL), A + DEC HL + ; and cursor up + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) +; Inp: HL - cursor pos +; -------------------------------------------------- +m40_up: + LD A, (HL) + SUB 10 ; 10 rows per symbol + JP NC, .up_no_minus + LD A, 240 ; wrap to bottom +.up_no_minus: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor right 8 pos) 20rows mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m20_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x3f ; wrap A=0..63 + LD (HL), A + CP 40 + DEC HL + RET M ; ret if column <40 + JP m40_wrap_rt ; or wrap to next line + +; -------------------------------------------------- +; Calculate VRAM address in 40 column mode +; -------------------------------------------------- +calc_addr_40: + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + CP 4 + LD B, A + JP M, .l2 + AND 0x3 + LD B, A + LD A, H + OR A + RRA + OR A + RRA + LD C, A + LD H, 0x6 + XOR A + +.l1: + ADD A, H + DEC C + JP NZ, .l1 + ADD A, B + +.l2: + ADD A, B + ADD A, 66 + LD H, A + LD A, 0x7 + LD (M_VARS.esc_var0),A + RET + +m2_lf: + LD A, (HL) + ADD A, 10 + CP 15 + JP NC, .lf_nowr + LD (HL), A + RET + +.lf_nowr: + LD A, (M_VARS.row_shift) + LD L, A + ADD A, ASCII_LF + LD E, A + LD C, ASCII_BS + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.cas_l5: + LD B, 0x40 + LD H, 0x40 ; TODO: LD H, B save 1b 3t + LD D, H + +.cas_l6: + LD A, (DE) + LD (HL), A + INC H + INC D + DEC B + JP NZ, .cas_l6 + INC L + INC E + DEC C + JP NZ, .cas_l5 + LD C, 10 + LD A, (M_VARS.row_shift) + ADD A, 8 + LD E, A + +.cas_l7: + LD B, 0x40 + LD D, 0x40 ; TODO: LD D, B save 1b 3t + XOR A + +.cas_l8: + LD (DE),A + INC D + DEC B + JP NZ,.cas_l8 + INC E + DEC C + JP NZ,.cas_l7 + LD A,0x0 + OUT (SYS_DD17PB),A + RET + + +; --------------------------------------------------- +; Handle ASCII_BS (cursor left) in 20row mode +; --------------------------------------------------- +m20_bksp: + INC HL + LD A, (HL) + OR A + DEC HL + RET Z + + INC HL + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x3f + LD (HL), A + DEC HL + RET + +; --------------------------------------------------- +; Print symbol in 64x25 mode +; --------------------------------------------------- +mp_mode_64: + CP 3 ; + JP Z, mp_mode_80 ; jump for screen_mode=3 + ; calc symbol address in VRAM + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + ADD A, 0x40 + LD H, A + ; + LD C, 7 ; symbol height + + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + EX DE, HL + XOR A + LD (DE), A + INC E + +.next_row: + LD A, (HL) + ADD A, A + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .next_row + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + ; draw cursor at end + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_rt: + INC HL + LD A, (HL) + ADD A, 1 + AND 0x3f ; wrap + LD (HL), A + DEC HL + RET NZ ; ret if no wrap + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Scroll Up for 10 rows +; -------------------------------------------------- +scroll_up: + LD A, (M_VARS.row_shift) + ADD A, 10 + OUT (SYS_DD17PA), A ; Scroll via VShift register + LD (M_VARS.row_shift), A ; store new VShift value + ; calc bottom 16 rows address in VRAM + LD HL, 0x40f0 ; 240th VRAM byte + ADD A, L + LD L, A + LD C, H + + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + XOR A + LD DE, 0x1040 ; D=16 E=64 (512/8 bytes in row) + +.next_row: + LD H, C + LD B, E + + ; clear 64 bytes (512px in mono or 256px in color mode) +.next_col: + LD (HL), A + INC H ; next column + DEC B + JP NZ, .next_col + INC L ; next row address + DEC D ; row counter - 1 + JP NZ, .next_row + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_bs: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x3f ; wrap column (0..63) + LD (HL), A + CP 63 + DEC HL + RET NZ + ; cursor up if wrapped + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_up: + LD A, (HL) + SUB 10 + JP NC, .no_wrap + LD A, 240 + +.no_wrap: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor column + 8) in 64x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m64_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x38 + LD (HL), A + DEC HL + RET NZ ; return if no wrap + ; cursor down if wrap + JP m64_lf + +; -------------------------------------------------- +; Print symbols in 80x25 mode +; -------------------------------------------------- +mp_mode_80: + CALL calc_addr_80 + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + ; fix address + EX DE, HL + INC E + ; make bitmask + LD A, B + OR A + JP Z, .l1 + DEC A + JP Z, .l2 + DEC A + JP Z, .l3 + JP .l4 + +.l1: + LD B, (HL) + LD A, (DE) + AND 0xc0 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .l1 + JP .l6 +.l2: + LD A, (HL) + RRCA + RRCA + AND 0x7 + LD B, A + LD A, (DE) + AND 0xf0 + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + AND 0xc0 + LD B, A + DEC D + LD A, (DE) + AND 0x1f + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, .l2 + JP .l6 +.l3: + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0x1 + LD B, A + LD A, (DE) + AND 0xfc + OR B + LD (DE), A + LD A, (HL) + RRCA + RRCA + RRCA + RRCA + AND 0xf0 + LD B, A + DEC D + LD A, (DE) + AND 0x7 + OR B + LD (DE), A + INC D + INC HL + INC E + DEC C + JP NZ, .l3 + JP .l6 +.l4: + DEC D +.l5: + LD A, (HL) + RLCA + RLCA + LD B, A + LD A, (DE) + AND 0x1 + OR B + LD (DE), A + INC HL + INC E + DEC C + JP NZ, .l5 + INC D + +.l6: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + ; Draw cursor after symbol + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + +; -------------------------------------------------- +; Handle ASCII_CAN (cursor right) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_rt: + INC HL + LD A, (HL) + ADD A, 1 ; inc column + AND 0x7f + LD (HL), A + CP 80 + DEC HL + RET M ; return if no wrap + +m80_col_wrap: + INC HL + XOR A + LD (HL), A + DEC HL + ; and move cursor to next row + +; -------------------------------------------------- +; Handle ASCII_LF (cursor down) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_lf: + LD A, (HL) + ADD A, 10 + CP 248 + JP NC, scroll_up + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_BS (cursor left) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_bs: + INC HL + LD A, (HL) + SUB 1 ; TODO: DEC A - save 1b 2t + AND 0x7f ; mask [0..127] + CP 127 + JP Z, .wrap + LD (HL), A + DEC HL + RET + +.wrap: + LD A, 79 + LD (HL), A + DEC HL + ; and move cursor to previous line + +; -------------------------------------------------- +; Handle ASCII_EM (cursor up) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_up: + LD A, (HL) + SUB 10 + JP NC, .no_wrap + LD A, 240 + +.no_wrap: + LD (HL), A + RET + +; -------------------------------------------------- +; Handle ASCII_TAB (cursor column + 8) in 80x25 mode +; Inp: HL - cursor pos +; -------------------------------------------------- +m80_tab: + INC HL + LD A, (HL) + ADD A, 8 + AND 0x7f + LD (HL), A + CP 80 + DEC HL + RET M ; return if no cursor wrap + JP m80_col_wrap + +; -------------------------------------------------- +; Calculate address for cursor pos for 80x25 mode +; Out: HL -> VRAM +; B -> pixel pos in byte +; -------------------------------------------------- +calc_addr_80: + LD HL, (M_VARS.cursor_row) + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + LD A, H + CP 4 + LD B, A + JP M, mns_ep_fm_0 + AND 3 + LD B, A + LD A, H + OR A + RRA + OR A + RRA + LD C, A + LD H, 3 + XOR A +mns_l1: + ADD A, H + DEC C + JP NZ, mns_l1 + ADD A, B +mns_ep_fm_0: + ADD A, 0x42 + LD H, A + LD C, 0x7 + RET + +; -------------------------------------------------- +; Clear screen and set cursor to 0,0 +; Inp: HL -> cursor position +; -------------------------------------------------- +m_clear_screen: + LD A, (M_VARS.screen_mode) + AND 0x8 + JP NZ, m_clear_20_rows ; for bit 4 is set, clear only 20 rows + ; all in black + LD A, 01111111b + OUT (VID_DD67PB), A ; C/M=1 FL=111 CL=111 All black + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD DE, video_ram + EX DE, HL + LD A, H + ADD A, 0x40 ; A=0x80 + LD B, 0 + +.fill_scrn: + LD (HL), B + INC HL + CP H + JP NZ, .fill_scrn ; fill while HL<0x8000 + + EX DE, HL + LD A, (M_VARS.cur_palette) + LD B, A ; B = current palette + LD A, (M_VARS.screen_mode) + AND 0x3 ; color? + LD A, 0x0 + JP NZ, .mono_mode + LD A, 01000000b +.mono_mode: + OR B + ; Restore mode and palette + OUT (VID_DD67PB), A + + ; And set cursor to home position + +; -------------------------------------------------- +; Set cursor to 0,0 and close VRAM access +; Inp: HL -> cursor_row +; -------------------------------------------------- +m_cursor_home: + XOR A + NOP + NOP + LD (HL), A + INC HL + XOR A + LD (HL), A + DEC HL + ;XOR A + LD A, 0 + ; Disable VRAM access + OUT (SYS_DD17PB), A + RET + +; Clear only 20 rows +m_clear_20_rows: + ; take row shift in account + LD A, (M_VARS.row_shift) + LD L, A + LD C, 20 + + ; Access VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.next_row: + LD H, 0x40 ; HL = 0x4000 + shift_row + LD B, 64 ; 64 bytes at row + XOR A +.next_col: + LD (HL), A + INC H ; next column + DEC B + JP NZ, .next_col + INC L ; next row + DEC C + JP NZ, .next_row + ; Disabe VRAM access + LD A, 0 + OUT (SYS_DD17PB), A + JP m_cursor_home + +; -------------------------------------------------- +; Draw cursor at current cursor position +; if not hidden +; -------------------------------------------------- +m_draw_cursor: + LD A, (M_VARS.screen_mode) + AND 0x4 ; check hidden cursor bit + RET NZ ; return if hidden + LD A, (M_VARS.screen_mode) + AND 0x3 ; check color mode (40 column mode 6x7 font) + JP NZ, .dc_mode_64 + + EX DE, HL + LD HL, (M_VARS.cursor_row) + LD A, H ; cursor column + CP 40 ; > 40? + EX DE, HL + RET P ; ret if column out of screen + + PUSH HL + EX DE, HL + CALL calc_addr_40 + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; previous address + DEC H + DEC H + INC L + LD C, 7 ; cursor size + ; build masks + LD A, B + OR B + JP Z, .dc_rt2 + DEC B + JP Z, .dc_mid + DEC B + JP Z, .dc_lt + JP .dc_rt1 +.dc_rt2: + INC H + INC H + LD DE, 0x001f + JP .dc_put +.dc_mid: + LD DE, 0x07c0 + JP .dc_put +.dc_lt: + LD DE, 0x01f0 + JP .dc_put +.dc_rt1: + LD DE, 0x007c + +.dc_put: + ; xor cursor mask with VRAM[HL] value + ; left bytes + LD A, (HL) + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR E + LD (HL), A + ; right bytes + INC H + LD A, (HL) + XOR D + LD (HL), A + INC H + LD A, (HL) + XOR D + LD (HL), A + ; next cursor row address + INC L + DEC H + DEC H + DEC H + DEC C + JP NZ, .dc_put ; draw next cursor row if c>0 + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + RET + + ; draw cursor in 64 column mode +.dc_mode_64: + CP 3 ; screen_mode = 3 - 80 rows + JP Z, .dc_mode_80 + EX DE, HL + LD HL, (M_VARS.cursor_row) ; H - col, L - row + ; take into account the vertical shift + LD A, (M_VARS.row_shift) + ADD A, L + LD L, A + ; + LD A, H + CP 64 ; check column + EX DE, HL + RET P ; return if column out of screen + EX DE, HL + ; calc VRAM address + ADD A, 0x40 + LD H, A + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD BC, 0x7f08 ; B=01111111b - mask, C=8 - cursor size +.cur_64_next: + ; xor with VRAM content + LD A, (HL) + XOR B + LD (HL), A + ; next row address + INC L + DEC C + JP NZ, .cur_64_next + EX DE, HL + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + + ; draw cursor in 80 column mode +.dc_mode_80 + EX DE, HL + LD HL, (M_VARS.cursor_row) + + LD A, H + CP 80 + EX DE, HL + RET P ; return if column > 80 + + PUSH HL + CALL calc_addr_80 + LD C, 7 ; cursor size + INC L + + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; mask + LD A, B + OR A + LD B, 0x1f + JP Z, .dc_1_byte + DEC A + LD DE, 0xc007 + JP Z, .dc_2_byte + DEC A + LD DE, 0xf001 + JP Z, .dc_2_byte + LD B, 0x7c + DEC H + JP .dc_1_byte ; TODO: unused + +.dc_1_byte: + ; xor with VRAM byte + LD A, (HL) + XOR B + LD (HL), A + INC L + DEC C + JP NZ, .dc_1_byte + JP .dc_80_end + +.dc_2_byte: + ; xor with previous byte + DEC H + LD A, (HL) + XOR D + LD (HL), A + ; xor with current byte + INC H + LD A, (HL) + XOR E + LD (HL), A + ; next cursor address + INC L + DEC C + JP NZ, .dc_2_byte + +.dc_80_end: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + RET + +; -------------------------------------------------- +; If ESC character, turn esc_mode ON +; Inp: A - ASCII symbol +; -------------------------------------------------- +m_handle_esc_code: + CP ASCII_ESC + JP NZ, m_handle_control_code + ; turn on ESC mode for next chars + LD HL, M_VARS.esc_mode + LD (HL), 0x1 ; turn on ESC mode + INC HL + LD (HL), 0xff ; esc_cmd = 0xff + RET + +; -------------------------------------------------- +; Handle one byte ASCII control code +; Inp: A - ASCII symbol +; -------------------------------------------------- +m_handle_control_code: + CP ASCII_BELL + JP Z, m_beep + LD HL, m_draw_cursor + PUSH HL + LD HL, M_VARS.cursor_row + PUSH AF + CALL m_draw_cursor + LD A, (M_VARS.screen_mode) + AND 0x08 ; 20-rows mode? + JP Z, handle_cc_common ; jump for normal screen modes + + ; for hidden cursor modes + POP AF + CP ASCII_TAB ; TAB + JP Z, m20_tab + CP ASCII_BS ; BKSP + JP Z, m20_bksp + CP ASCII_CAN ; Cancel + JP Z, m40_rt + CP ASCII_US ; ASCII Unit separator + JP Z, m_clear_20_rows + CP ASCII_LF ; LF + JP Z, m2_lf + CP ASCII_CR ; CR + RET NZ ; ret on unknown + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; Handle cursor for 40x25, 64x25, 80x25 modes +; -------------------------------------------------- +handle_cc_common: + POP AF + CP ASCII_US + JP Z, m_clear_screen + CP ASCII_FF + JP Z, m_cursor_home + PUSH AF + LD A, (M_VARS.screen_mode) + AND 3 ; check for color modes + JP NZ, .handle_cc_mono + ; 32x25 text mode + POP AF + CP ASCII_TAB ; cursor right +8 + JP Z, m20_tab + CP ASCII_BS ; cursor left + JP Z, m40_bksp + CP ASCII_CAN ; cursor right + JP Z, m40_rt + CP ASCII_EM ; cursor up + JP Z, m40_up + CP ASCII_SUB + JP Z, m40_lf ; cursor down + CP ASCII_LF + JP Z, m40_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 ; move cursor to first column for CR + DEC HL + RET + +; -------------------------------------------------- +; Handle control chars for 64x25 or 80x25 modes +; -------------------------------------------------- +.handle_cc_mono: + LD A, (M_VARS.screen_mode) + CP 3 + JP Z, handle_cc_80x25 + CP 7 + JP Z, handle_cc_80x25 + ; 64x25 screen mode + POP AF + CP ASCII_TAB + JP Z, m64_tab + CP ASCII_BS + JP Z, m64_bs + CP ASCII_CAN + JP Z, m64_rt + CP ASCII_EM + JP Z, m64_up + CP ASCII_SUB + JP Z, m64_lf + CP ASCII_LF + JP Z, m64_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; Handle control chars for 80x25 modes +; -------------------------------------------------- +handle_cc_80x25: + POP AF + CP ASCII_TAB + JP Z, m80_tab + CP ASCII_BS + JP Z, m80_bs + CP ASCII_CAN + JP Z, m80_rt + CP ASCII_EM + JP Z, m80_up + CP ASCII_SUB + JP Z, m80_lf + CP ASCII_LF + JP Z, m80_lf + CP ASCII_CR + RET NZ + INC HL + LD (HL), 0x0 + DEC HL + RET + +; -------------------------------------------------- +; +; -------------------------------------------------- +m_beep: + LD HL, (M_VARS.beep_duration) + EX DE, HL + LD HL, (M_VARS.beep_period) + LD A, 00110110b ; TMR#0 LSB+MSB Square Wave Generator + OUT (TMR_DD70CTR), A + LD A, L ; LSB + OUT (TMR_DD70C1), A + LD A, H ; MSB + OUT (TMR_DD70C1), A + LD A, (M_VARS.strobe_state) + LD B, A +m_bell_cont: + LD A, D ; DE=duration + OR E + RET Z ; ret if enough + DEC DE + LD A, B + XOR BELL_PIN + LD B, A + OUT (DD67PC), A ; Invert bell pin +m_bell_wait_tmr1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; 0x10 + JP NZ, m_bell_wait_tmr1 + LD A, B + XOR BELL_PIN ; Flip pin again + LD B, A + OUT (DD67PC), A +m_bell_wait_tmr2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP Z,m_bell_wait_tmr2 + JP m_bell_cont + + +; ------------------------------------------------------ +; 5 Set cursor position +; ------------------------------------------------------ +esc_set_cursor: + CALL m_draw_cursor + LD DE, M_VARS.esc_param + LD HL, M_VARS.cursor_col + INC DE + LD A, (DE) ; column + SUB 32 + LD B, A + LD A, (M_VARS.screen_mode) + CP 3 + JP Z, .mode_80 + CP 7 + JP Z, .mode_80 + OR A + JP Z, .mode_40 + CP 4 + JP Z, .mode_40 + ; mode 64x25 + LD A, B + CP 64 + JP M, .common + LD A, 64 + JP .common + ; mode 40x25 +.mode_40: + LD A, B + CP 40 + JP M, .common + LD A, 40 + JP .common + ; mode 80x25 +.mode_80: + LD A, B + CP 80 + JP M, .common + LD A, 80 +.common: + LD (HL), A + DEC DE + DEC HL + LD A, (DE) + SUB 32 + CP 24 + JP C, esc_le_24 + LD A, 24 +esc_le_24: + LD B, A + ADD A, A + ADD A, A + ADD A, B + ADD A, A + LD (HL), A + CALL m_draw_cursor ; TODO change call+ret to jp + RET ; + +; ------------------------------------------------------ +; 6n Set video mode or cursor visibility +; Inp: n is +; 0 - C 32x25 with cursor; 0000 +; 1 - M 64x25 with cursor; 0001 +; 2 - M 64x25 with cursor; 0010 +; 3 - M 80x25 with cursor; 0011 +; 4 - C 32x25 no cursor; 0100 +; 5 - M 64x25 no cursor; 0101 +; 6 - M 64x25 no cursor; 0110 +; 7 - M 80x25 no cursor; 0111 +; 8 - M 20rows mode 1000 +; 9 - hide cursor 1001 +; 10 - show cursor 1010 +; ------------------------------------------------------ +esc_set_vmode: + LD HL, M_VARS.screen_mode + LD A, (M_VARS.cur_palette) + LD B, A + LD A, (M_VARS.esc_param) ; first parameter - video mode + AND 0xf + CP 11 + RET NC ; return if not valid input parameter + CP 9 + JP Z, .cursor_hide + CP 10 + JP Z, .cursor_show + LD (HL), A ; store new mode + CP 4 + JP Z, .set_color_mode + AND 0x3 ; monochrome (80x25, 64x25) mode? + LD A, 0 ; mode 512x254 mono + JP NZ, .skip_for_mono_mode + ; mode=0 or 4 -> 256x256px color +.set_color_mode: + LD A, 0x40 ; color mode +.skip_for_mono_mode: + OR B ; color mode with palette + OUT (VID_DD67PB), A ; configure screen mode + + LD HL, M_VARS.cursor_row + CALL m_clear_screen + +.draw_cursor: + CALL m_draw_cursor ; TODO change call+ret to jp + RET + +.cursor_hide: + LD A, (HL) ; screen_mode + OR 00000100b ; cursor hide + LD (HL), A + LD HL, M_VARS.cursor_row + JP .draw_cursor + +.cursor_show: + LD A, (HL) ; screen_mode + AND 11111011b ; cursor show + LD (HL), A + JP .draw_cursor + + +; ------------------------------------------------------ +; 4n n=1..4 Set drawing color +; ------------------------------------------------------ +esc_set_color: + LD A, (M_VARS.esc_param) +m_set_color: + AND 0x3 + RRA + LD B, A + LD A, 0x0 ; TODO: unused + SBC A, A + LD (M_VARS.curr_color), A + LD A, B + DEC A + CPL + LD (M_VARS.curr_color+1), A + RET + +;--------------------------------------------------- +; Print symbol or print sprite at X,Y coordinates +; Inp: param x,y +; C - character or sprite_no to draw +;--------------------------------------------------- +m_print_at_xy: + ; check video mode + LD A, (M_VARS.screen_mode) + AND 0x3 ; color? + JP NZ, esc_exit ; exit for mono modes + + LD A, C + AND 0x7f + LD C, A ; C = C with 7th bit reset + CP 0x1 + JP Z, .sprites_en ; enable sprite mode + + CP ASCII_SP + JP M, mode2_exit ; codes 0..31 - turm off game_mode + + ; check X, Y range to prevent drawing symbols out of screen + LD HL, M_VARS.esc_param + LD A, (HL) + LD E, A + ADD A, 8 + JP C, mode2_exit ; exit if esc_param[0]>247 + LD (HL), A + INC HL ; HL -> esc_param[1] + LD A, 247 + CP (HL) + JP C, mode2_exit ; exit if esc_param[1]>247 + ; calculate X,Y pixel address in VRAN + LD D, (HL) + CALL calc_px_addr + ; HL - address, B - pixel pos in byte + LD A, L + SUB 8 + LD L, A + PUSH HL ; save address + + LD A, (M_VARS.esc_var2) + OR A + JP NZ, .mode_sp + + ; font + LD A, C + CALL m_get_glyph + LD C, 7 + POP DE + INC E + JP .out_sp + + ; sprite mode +.mode_sp: + LD A, C + SUB 32 + CP 35 + JP NC, co_ex_l08 + + ; Calc sprite address + LD L, A ; HL=A - sprite_no + XOR A + LD H, A + ADD HL, HL + ADD HL, HL + ADD HL, HL ; HL=HL*8 + LD DE, game_sprite_tab + ADD HL, DE ; HL -> sprite + LD C, 8 ; bytes count + POP DE + + ; Out sprite + ; DE -> VRAM address + ; C - height +.out_sp: + LD A, (M_VARS.esc_param+2) + DEC A + JP Z, out_no_xor + +.next_line: + PUSH HL + ; Access Video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD L, (HL) ; load from table + LD H, 0x0 + LD A, B + OR A + JP Z, .l05 +.l04: + ADD HL, HL + DEC A + JP NZ, .l04 +.l05: + EX DE, HL + LD A, (M_VARS.curr_color) + AND E + XOR (HL) + LD (HL), A + INC H + INC H + LD A, (M_VARS.curr_color) + AND D + XOR (HL) + LD (HL), A + DEC H + LD A, (M_VARS.curr_color+1) + AND E + XOR (HL) + LD (HL), A + INC H + INC H + LD A, (M_VARS.curr_color+1) + AND D + XOR (HL) + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + ; Disable VRAM + LD A, 0x0 + OUT (SYS_DD17PB), A + POP HL + INC HL + DEC C + JP NZ, .next_line + RET + +.sprites_en: + LD (M_VARS.esc_var2), A + RET + +mode2_exit: + XOR A + LD (M_VARS.esc_var2), A + JP esc_exit + +co_ex_l08: + POP DE + JP mode2_exit + +out_no_xor: + PUSH HL + ; Acess to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD L, (HL) + LD H, 0x0 + LD A, B + OR A + JP Z, .l11 +.l10: + ADD HL, HL + DEC A + JP NZ, .l10 +.l11: + EX DE, HL + PUSH BC + LD A, (M_VARS.curr_color) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + LD A, (M_VARS.curr_color+1) + CPL + LD B, A + LD A, (HL) + XOR B + OR E + XOR B + LD (HL), A + INC H + INC H + LD A, (HL) + XOR B + OR D + XOR B + LD (HL), A + DEC H + DEC H + DEC H + INC L + EX DE, HL + POP BC + + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP HL + INC HL + DEC C + JP NZ, out_no_xor + RET + +game_sprite_tab: + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; 0x00 + DB 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E ; 0x01 + DB 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E ; 0x02 + DB 0x00, 0x08, 0x08, 0x14, 0x63, 0x14, 0x08, 0x08 ; 0x03 + DB 0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00 ; 0x04 + DB 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00 ; 0x05 + DB 0x1C, 0x3E, 0x1C, 0x7F, 0x7F, 0x6B, 0x08, 0x1C ; 0x06 + DB 0x08, 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x08, 0x1C ; 0x07 + DB 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18 ; 0x08 + DB 0x18, 0xDB, 0x3C, 0xE7, 0xE7, 0x3C, 0xDB, 0x18 ; 0x09 + DB 0xE7, 0xE7, 0x00, 0x7E, 0x7E, 0x00, 0xE7, 0xE7 ; 0x0a + DB 0x7E, 0x81, 0x81, 0xFF, 0xFF, 0x81, 0x81, 0x7E ; 0x0b + DB 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18 ; 0x0c + DB 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00 ; 0x0d + DB 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00 ; 0x0e + DB 0x00, 0x08, 0x0C, 0xFE, 0xFE, 0x0C, 0x08, 0x00 ; 0x0f + DB 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 ; 0x10 + DB 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 ; 0x11 + DB 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0 ; 0x12 + DB 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F ; 0x13 + DB 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ; 0x14 + DB 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF ; 0x15 + DB 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F ; 0x16 + DB 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0 ; 0x17 + DB 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 ; 0x18 + DB 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 ; 0x19 + DB 0x70, 0x08, 0x76, 0xFF, 0xFF, 0xFF, 0x7E, 0x18 ; 0x1a + DB 0xC3, 0xDB, 0xDB, 0x18, 0x18, 0xDB, 0xDB, 0xC3 ; 0x1b + DB 0xFC, 0xCC, 0xFC, 0x0C, 0x0C, 0x0E, 0x0F, 0x07 ; 0x1c + DB 0xFE, 0xC6, 0xFE, 0xC6, 0xC6, 0xE6, 0x67, 0x03 ; 0x1d + DB 0x18, 0x3C, 0x3C, 0x18, 0x7E, 0x18, 0x24, 0x66 ; 0x1e + DB 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 ; 0x1f + DB 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 ; 0x20 + DB 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 0x21 + DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF ; 0x22 + +; -------------------------------------------------- +; Calculate address of pixel in Video RAM +; Inp: DE - Y, X +; Out: HL - address +; B - offset in byte +; -------------------------------------------------- +calc_px_addr: + ; take into account the vertical displacement + LD A, (M_VARS.row_shift) + SUB D + DEC A + LD L, A + + LD A, E + AND 0x07 ; X mod 8 - offset in byte + LD B, A + + LD A, E + RRA + RRA + AND 00111110b + ADD A, 0x40 ; VRAM at 0x4000 + LD H, A + RET + + +;--------------------------------------------------- +; Draw filled rectanger +; Inp: esc param X1,Y2,X2,Y2 +; -------------------------------------------------- +esc_draw_fill_rect: + LD HL, M_VARS.esc_param + LD E, (HL) ; E=X1 + INC HL + LD C, (HL) ; C=Y1 + INC HL + INC HL + LD D, (HL) ; D=Y2 + LD A, D + SUB C ; delta Y + JP NZ, .non_zero_h + INC A ; 1 as minimum +.non_zero_h: + LD C, A ; C = height + ; DE = Y2, X1 + CALL calc_px_addr + ; HL -> videomem offset, b - pixel offset + + ; build pixel mask + XOR A +.shift_mask_l: + SCF + RLA + DEC B + JP P, .shift_mask_l + RRA + LD (M_VARS.pixel_mask_l), A + CPL ; invert + LD (M_VARS.pixel_mask_l_i), A + LD A, (M_VARS.esc_param+2) ; X2 + AND 0x7 ; 0..7 + LD B, A + XOR A +.shift_mask_r: + SCF + RLA + DEC B + JP P, .shift_mask_r + LD (M_VARS.pixel_mask_r_i), A + LD B, C + ; calc end address + LD A, (M_VARS.esc_param+2) ; X2 + RRA + RRA + AND 00111110b + ADD A, 0x40 + SUB H + RRCA + LD C, A ; C - width + INC B + LD A, (M_VARS.esc_param+4) + DEC A + JP NZ, .rectangle_xor + LD A, (M_VARS.pixel_mask_r_i) + CPL + LD (M_VARS.pixel_mask_r), A + LD D, A + LD A, (M_VARS.pixel_mask_l) + LD E, A + ; draw B horisontal lines +.next_line: + PUSH DE + PUSH HL + PUSH BC + CALL draw_line_h + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, .next_line + RET + + ; draw B horisontal lines (xor) +.rectangle_xor: + LD A, (M_VARS.pixel_mask_r_i) + LD (M_VARS.pixel_mask_r), A + LD D, A + LD A, (M_VARS.pixel_mask_l_i) + LD E, A + ; Access to VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + +.edf_l6: + PUSH DE + PUSH HL + PUSH BC + LD A, C + OR A + JP NZ, .w_ne_0 ; jump if width != 0 + LD A, E ; merge masks E=E or D + OR D +.next_8px: + LD E, A +.w_ne_0: + LD B, E ; B - mask + EX DE, HL + LD HL, (M_VARS.curr_color) ; color + EX DE, HL + ; Set pixels - VideoRAM[HL] = color xor VideoRAM[HL] + LD A, E + AND B + XOR (HL) + LD (HL), A + ; And next byte + INC H + LD A, D + AND B + XOR (HL) + LD (HL), A + ; ---------- + INC H + LD A, C + OR A + JP Z, .complete + DEC C + ; right tail of line, use right mask +.r_mask: + LD A, (M_VARS.pixel_mask_r) + JP Z, .next_8px + ; full 8 bits without mask at middle of line +.next_full: + LD A, (HL) + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + LD (HL), A + INC H + DEC C + JP NZ, .next_full + JP .r_mask +.complete: + POP BC + POP HL + POP DE + INC L + DEC B + JP NZ, .edf_l6 + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +;--------------------------------------------------- +; Paint screen +; Inp: params X,Y,Color,repColor +;--------------------------------------------------- +esc_paint: + ; Save stack + LD HL, 0x0 + ADD HL, SP + LD (M_VARS.paint_sp_save), HL + + ; Set our own stack + LD HL, M_VARS.paint_stack ; TODO: Z80 LD SP,var i800 - LXI SP,nn + LD SP, HL + + ; save current color + LD HL, (M_VARS.curr_color) + LD (M_VARS.tmp_color), HL + + ; set color from param 3 + LD A, (M_VARS.esc_param+2) + DEC A + CALL m_set_color + + ; color to replace, from param 4 + LD A, (M_VARS.esc_param+3) + DEC A + LD (M_VARS.cmp_color), A + + ; HL - Y,X + LD A, (M_VARS.esc_param) + LD L, A + LD A, (M_VARS.esc_param+1) + LD H, A + LD (M_VARS.paint_y), A + + LD A, (M_VARS.esc_param+4) ; 0 - full fill, 1 - fast fill + DEC A + LD (M_VARS.esc_param), A + + LD A, 0x2 + LD (M_VARS.paint_var5), A ; task_no=2 + + EX DE, HL + CALL calc_px_addr + LD (M_VARS.esc_param+1), HL ; temporary ctore address of start fill point + ; make mask + LD A, 10000000b +.l1: + RLCA + DEC B + JP P, .l1 + + LD B, A + LD (M_VARS.esc_param+3), A ; store mask + + ; find left border + LD A, (M_VARS.cmp_color) + LD C, A + LD D, E ; D = X + CALL paint_find_left + ; find right border + LD HL, (M_VARS.esc_param+1) ; restore HL + LD A, (M_VARS.esc_param+3) ; restore mask + LD B, A + CALL paint_find_right + ; + LD HL, 0x0 + PUSH HL + PUSH HL + ; + LD A, (M_VARS.esc_param) ; A = fill mode + OR A + JP Z, ep_fm_0 + ; push fill task parameters + LD A, (M_VARS.paint_var5) + DEC A ; task_no-1 + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + +ep_fm_0: + ; push fill task parameters + LD A, (M_VARS.paint_var5) ; task_no + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + JP paint_task ; exec task + +ep_task_end: + LD A, (M_VARS.cmp_color) + LD C, A ; color to compare + + LD A, (M_VARS.esc_param) ; fill mode 0 - full, 1 - fast + OR A + JP NZ, ep_f_fast + + LD A, 0x2 + LD (M_VARS.paint_var7), A + LD A, (M_VARS.paint_var2) + CP 2 + JP Z, ep_l4 + JP ep_l5 ; TODO: change to one JP NZ + +ep_l4: + LD A, 1 + LD (M_VARS.paint_var5), A ; task_no? + JP ep_l8 + +ep_l5: + LD A, 2 + LD (M_VARS.paint_var5), A + JP ep_l11 + +ep_l6: + LD A, (M_VARS.paint_var7) + OR A + JP Z, paint_task + LD A, (M_VARS.paint_var2) + CP 2 + JP Z, ep_l5 ; TODO: change to one JP NZ + JP ep_l4 + +ep_f_fast: + LD A, (M_VARS.paint_var2) + LD (M_VARS.paint_var5), A + CP 1 ; TODO: DEC A - save 1b 3t + JP Z, ep_l8 ; TODO: change to one JP NZ + JP ep_l11 + +ep_l8: + LD A, (M_VARS.paint_var3) + LD D, A + LD A, (M_VARS.paint_var1) + LD E, A + LD HL, (M_VARS.esc_param+1) + LD A, (M_VARS.esc_param+3) + LD B, A + LD A, (M_VARS.paint_var4) + DEC A + JP Z, ep_l10 + LD (M_VARS.paint_y), A + INC L + CALL paint_find_next_right + JP Z, ep_l10 + LD HL, (M_VARS.esc_param+4) + LD A, (M_VARS.esc_param+6) + LD B, A + INC L + CALL paint_find_next_left + JP Z, ep_l10 + LD A, (M_VARS.esc_param) + OR A + JP NZ, ep_l9 + JP ep_l12 + +ep_l9: + LD A, (M_VARS.paint_var5) + LD H, A + LD L, E + PUSH HL + LD A, (M_VARS.paint_y) + LD H, A + LD L, D + PUSH HL + JP paint_task + +ep_l10: + LD A, (M_VARS.esc_param) + OR A + JP NZ, paint_task + LD A, (M_VARS.paint_var7) + DEC A + LD (M_VARS.paint_var7), A + JP ep_l6 + +ep_l11: + LD A, (M_VARS.paint_var3) + LD D, A + LD A, (M_VARS.paint_var1) + LD E, A + LD HL, (M_VARS.esc_param+1) + LD A, (M_VARS.esc_param+3) + LD B, A + LD A, (M_VARS.paint_var4) + INC A + CP 0xff + JP Z, ep_l10 + LD (M_VARS.paint_y), A + DEC L + CALL paint_find_next_right + JP Z, ep_l10 + LD HL, (M_VARS.esc_param+4) + LD A, (M_VARS.esc_param+6) + LD B, A + DEC L + CALL paint_find_next_left + JP Z, ep_l10 + LD A, (M_VARS.esc_param) + OR A + JP NZ, ep_l9 + JP ep_l12 + +; --------------------------------------------------- +; +; --------------------------------------------------- +paint_find_next_right: + CALL get_pixel + JP NZ, .l1 + CALL paint_find_left + LD A, 0xff + OR A + RET + +.l1: + LD A, D + CP E + RET Z + INC D + LD A, B + RLCA + LD B, A + JP NC, .l2 + INC H + INC H +.l2: + CALL get_pixel + JP NZ, .l1 + LD A, 0xff + OR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +paint_find_next_left: + CALL get_pixel + JP NZ, .l1 + CALL paint_find_right + LD A, 0xff + OR A + RET +.l1: + LD A, E + CP D + RET Z + DEC E + LD A, B + RRCA + LD B, A + JP NC, .l2 + DEC H + DEC H +.l2: + CALL get_pixel + JP NZ, .l1 + LD A, 0xff + OR A + RET + +ep_l12: + LD A, D + LD (M_VARS.pixel_mask_r), A + LD A, (M_VARS.paint_var5) + LD D, A + PUSH DE + LD A, (M_VARS.pixel_mask_r) + CP E + JP NZ, ep_l13 + LD L, A + LD A, (M_VARS.paint_y) + LD H, A + PUSH HL + JP ep_l16 +ep_l13: + LD D, E + CALL paint_find_left + LD E, D + LD A, (M_VARS.paint_y) + LD D, A + PUSH DE + LD A, (M_VARS.pixel_mask_r) + LD D, A + CP E + JP Z, ep_l16 +ep_l14: + DEC E + LD A, B + RRCA + LD B, A + JP NC, ep_l15 + DEC H + DEC H +ep_l15: + CALL get_pixel + JP NZ, ep_l14 + JP ep_l12 +ep_l16: + JP ep_l10 + +; --------------------------------------------------- +; Find rightmost pixel to fill +; In/Out: E = x_right +; HL - current pixel address +; B - pixel mask +; --------------------------------------------------- +paint_find_right: + LD A, E + CP 0xff + RET Z ; return if X=right border + INC E ; x=x+1 + ; rotate pixel mask right + LD A, B + RLCA + LD B, A + JP NC, .in_byte + ; inc addr+2 (2 byte per 8 pixels) + INC H + INC H + +.in_byte: + CALL get_pixel + JP Z, paint_find_right ; find until same color + ; border found, x-1 + DEC E + ; rotate mask back 1 px + LD A, B + RRCA + LD B, A + RET NC + ; addr-2 if previous byte + DEC H + DEC H + RET + +; --------------------------------------------------- +; Find leftmost pixel to fill +; In/Out: D = x_left +; HL - current pixel address +; B - pixel mask +; --------------------------------------------------- +paint_find_left: + LD A, D + OR A + RET Z ; return if x=0 + + DEC D ; x-1 + LD A, B + RRCA ; rotate mask to right + LD B, A + JP NC, .in_byte + DEC H ; addr-2 (2 byte for 8px) + DEC H + +.in_byte: + CALL get_pixel + JP Z, paint_find_left ; repeat until same color + + INC D ; border found, x+1 + ; mask rotate right + LD A, B + RLCA + LD B, A + RET NC + ; if CF, inc address+2 + INC H + INC H + RET + +; --------------------------------------------------- +; Inp: HL - address +; B - pixel mask +; C - color to compare +; Out: A - 0,1,2 +; ZF - set if color match +; --------------------------------------------------- +get_pixel: + ; Access to VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; get pixel and mask + LD A, (HL) + AND B + JP NZ, .bit1_set + INC H + LD A, (HL) + DEC H + AND B + JP NZ, .bit2_set + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + CP C + RET + +.bit1_set: + INC H + LD A, (HL) + DEC H + AND B + JP NZ, .bit12_set + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 0x1 + CP C + RET + +.bit2_set: + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 0x2 + CP C + RET + +.bit12_set: + LD A, 0x0 + OUT (SYS_DD17PB), A + LD A, 3 + CP C + RET + +paint_task: + POP HL ; L=x0, H=Y + LD (M_VARS.paint_var3), HL + EX DE, HL + + POP HL ; L=x1, H=mode + LD A, H + OR A + JP Z, paint_exit ; jump for mode=0 + + ; calc leftmost pixel address, mask for draw horisontal line + LD (M_VARS.paint_var1), HL + CALL calc_px_addr + LD (M_VARS.esc_param+1), HL + LD C, B + LD A, 0x80 +.lmp_mask: + RLCA + DEC B + JP P, .lmp_mask + LD (M_VARS.esc_param+3), A + ; calc rightmos pixel address and mask + LD A, (M_VARS.paint_var1) + LD E, A + LD A, (M_VARS.paint_var4) + LD D, A + CALL calc_px_addr + LD (M_VARS.esc_param+4), HL + LD D, B + LD A, 0x80 +.rmp_mask: + RLCA + DEC B + JP P, .rmp_mask + LD (M_VARS.esc_param+6), A + LD A, (M_VARS.esc_param+3) ; TODO: unused code + + XOR A +.lmi_mask: + SCF + RLA + DEC C + JP P, .lmi_mask + RRA + LD E, A ; E - left inv mask + + XOR A +.rmi_mask: + SCF + RLA + DEC D + JP P, .rmi_mask + CPL + LD D, A ; D - right inv mask + + LD (M_VARS.pixel_mask_r), A + LD HL, (M_VARS.esc_param+1) ; HL -> lext pix address + LD A, (M_VARS.esc_param+5) ; right pix address (low byte) + SUB H ; delta x + RRCA ; 2 byte for 8 pix + LD C, A ; C - line width + CALL draw_line_h + JP ep_task_end + +paint_exit: + LD HL, (M_VARS.tmp_color) ; restore previous current color + LD (M_VARS.curr_color), HL + LD HL, (M_VARS.paint_sp_save) ; restore previous stack + LD SP, HL + RET + +;--------------------------------------------------- +; Draw horizontal line +; Inp: C - width +; DE - left & right pixel mask +; HL - address of first byte of line +;--------------------------------------------------- +draw_line_h: + ; Access to VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, C + OR A + JP NZ, .width_ne0 + LD A, E ; join left and right masks + OR D +.next_byte: + LD E, A +.width_ne0: + LD B, E + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Get pixels, apply colors + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A ; store first + ; Same for second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; move to next byte + INC H + LD A, C + OR A + JP Z, .complete + DEC C +.r_mask: + ; use right mask for last right byte + LD A, (M_VARS.pixel_mask_r) + JP Z, .next_byte +.full_8: + LD (HL), E + INC H + LD (HL), D + INC H + DEC C + JP NZ, .full_8 + JP .r_mask +.complete: ; TODO: duplicate close_vram_ret + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +;--------------------------------------------------- +; 2x1y1x2y2 Draw Line +;--------------------------------------------------- +esc_draw_line: + LD HL, M_VARS.esc_param + LD E, (HL) ; E=X1 + INC HL + LD D, (HL) ; D=Y1 + INC HL + LD A, (HL) + INC HL + LD H, (HL) ; H=Y2 + LD L, A ; L=X2 + CP E + JP C, .x1_le_x2 + EX DE, HL ; exchange if X1>X2 +.x1_le_x2: + LD (M_VARS.esc_param), HL ; store x1,y1 back + LD A, E + SUB L + LD L, A ; L - width + LD A, D + SUB H + LD H, A ; H - height + PUSH AF + JP NC, .pos_height + ; change sign + CPL + INC A + LD H, A +.pos_height: + EX DE, HL + LD HL, (M_VARS.esc_param) + EX DE, HL + JP Z, height0 + LD A, L + OR A + JP Z, .width0 + LD B, A + POP AF + LD A, 0x0 + ADC A, A + LD (M_VARS.esc_param+4), A + ; HL = E/B height/width + LD E, H + LD C, 16 + LD D, 0 +.next_16: + ADD HL, HL + EX DE, HL + ADD HL, HL + EX DE, HL + LD A, D + JP C, .edl_l4 + CP B + JP C, .edl_l5 +.edl_l4: + SUB B + LD D, A + INC HL +.edl_l5: + DEC C + JP NZ, .next_16 + LD DE, 0x0 + PUSH DE + ; save result at stack + PUSH HL + + LD HL, (M_VARS.esc_param) ; x1,y1 + EX DE, HL + LD C, B + CALL calc_px_addr + ; HL - address, B - offset in byte + ; make mask + LD A, 10000000b +.roll_l: + RLCA + DEC B + JP P, .roll_l + CPL + LD B, A ; b - inv mask + +.edl_l7 + POP DE + EX (SP), HL ; save HL on top of stack + LD A, H + ADD HL, DE + SUB H + CPL + INC A + EX (SP), HL + PUSH DE + PUSH BC + LD C, A + EX DE, HL + + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Access VideoRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + + LD A, (M_VARS.esc_param+4) ; sign of delta Y + OR A + JP NZ, .next_down +.next_up: + ; firs byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + DEC H + LD A, C + OR A + JP Z, .is_last + DEC C + ; draw up + DEC L + JP .next_up +.next_down: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; + DEC H + LD A, C + OR A + JP Z, .is_last + DEC C + ; draw down + INC L + JP .next_down +.is_last: + ; Disable VideoRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + POP BC + LD A, B + ; <<1px + SCF + RLA + JP C, .edl_l11 + RLA + INC H + INC H +.edl_l11 + LD B, A + DEC C + JP NZ, .edl_l7 + POP HL + POP HL + RET + +; -------------------------------------------------- +; draw vertical line +; Inp: DE - YX +; L - length +; -------------------------------------------------- +.width0 + LD C, H + CALL calc_px_addr + + ; make pixel mask + LD A, 10000000b +.edl_l13: + RLCA + DEC B + JP P, .edl_l13 + CPL + LD B, A + + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + POP AF + + ; Enable VRAM + LD A, 0x1 + OUT (SYS_DD17PB), A + JP C, .next_row_down + +.next_row_up: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next Y + DEC H + LD A, C + OR A + JP Z, close_vram_ret + DEC C + ; dec row + DEC L + JP .next_row_up + +.next_row_down: + ; first byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; second byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next address + DEC H + LD A, C + OR A + JP Z, close_vram_ret + DEC C + ; inc row + INC L + JP .next_row_down + +close_vram_ret + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; draw horizontal line +; Inp: DE - YX +; L - length +; -------------------------------------------------- +height0 + POP AF + LD C, L + LD A, L + OR A + JP NZ, .len_ne0 + INC C ; length 1 at least +.len_ne0: + CALL calc_px_addr + ; make pixel mask + LD A, 10000000b +.edl_l19 + RLCA + DEC B + JP P, .edl_l19 + CPL + LD B, A + + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + +.next_col: + ; set 1st byte + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; set 2nd byte + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; next byte + DEC H + ; next (right) horizontal pixel + LD A, B + SCF + RLA + JP C, .edl_l21 + RLA + INC H + INC H +.edl_l21 + LD B, A + DEC C + JP NZ, .next_col + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; -------------------------------------------------- +; ESC Draw Dot +; -------------------------------------------------- +esc_draw_dot: + LD HL, (M_VARS.esc_param) + EX DE, HL + CALL calc_px_addr + LD A, 0x80 +edd_l1 + RLCA + DEC B + JP P, edd_l1 + LD B, A + LD A, (M_VARS.esc_param+2) + CP 0x3 + JP Z, edd_ep_task_end + LD A, B + CPL + LD B, A + LD A, (M_VARS.esc_param+2) + CP 0x2 + JP Z, edd_ep_fm_0 + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + AND B + LD (HL), A + INC H + LD A, (HL) + AND B + LD (HL), A + LD A, 0x0 + OUT (SYS_DD17PB), A + RET +edd_ep_fm_0 + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + LD A, 0x0 + OUT (SYS_DD17PB), A + RET +edd_ep_task_end + CALL get_pixel + LD (M_VARS.esc_var3), A + RET + +; -------------------------------------------------- +; +; -------------------------------------------------- +esc_picture: + LD HL, (M_VARS.esc_param+3) + LD A, (HL) + CP ':' + RET NZ + INC HL + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD A, (HL) + LD (M_VARS.esc_var0), A + INC HL + LD A, (HL) + LD (M_VARS.esc_var1), A + INC HL + PUSH HL + LD C, (HL) + INC HL + LD B, (HL) + INC HL + INC HL + EX DE, HL + PUSH DE + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL pict_sub1 + ; Disable VRAM access + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP DE + POP HL + LD (HL), C + INC HL + LD (HL), B + CALL pict_sub2 ; TODO: replace call+ret to jp; + RET + +pict_sub1: + LD A, (M_VARS.esc_param) + CP ASCII_EM + JP Z, gih_up + CP ASCII_CAN + JP Z, gih_rt + CP ASCII_SUB + JP Z, gih_ctrl_z + CP ASCII_BS + JP Z, gih_bs + CP ASCII_US + JP Z, pict_clr + RET + +pict_clr: + LD L, C + LD H, B + LD A, (M_VARS.esc_var0) + LD C, A + LD A, (M_VARS.esc_var1) + LD B, A + CALL put_image + POP HL + POP HL + POP HL + RET + +ehd_l1: + CP 0x2 + JP Z, get_image_hdr + CP 0x3 + JP Z, m_fn_39 + RET + +; -------------------------------------------------- +; Function 39 +; -------------------------------------------------- +m_fn_39: + LD HL, (M_VARS.esc_param+2) ; pr 3,4 + INC L + LD C, L + LD B, H + LD E, L + CALL dc_mul_e_h + ADD HL, HL + EX DE, HL + LD HL, (M_VARS.esc_param) ; par 1,2 + PUSH BC + PUSH DE + LD BC, 10 + LD E, L + LD D, H + ADD HL, BC + EX DE, HL + LD (HL), E + INC HL + LD (HL), D + INC HL + POP BC + LD A, 4 +.l1: + PUSH AF ; TODO: remove AF not changed + EX DE, HL + ADD HL, BC + EX DE, HL + LD (HL), E + INC HL + LD (HL), D + INC HL + POP AF ; TODO: remove AF not changed + DEC A + JP NZ, .l1 + EX DE, HL + LD HL, 0x0 + ADD HL, BC ; HL=BC + ADD HL, BC ; HL=2*BC + ADD HL, HL ; HL=4*BC + ADD HL, BC ; HL=5*BC + ADD HL, HL ; HL=10*BC + EX DE, HL ; DE=10*BC + LD A, 0x0 + LD B, A ; B=A=0 + ; fill DE bytes at [HL] with 0 +.l2: + LD (HL), B + INC HL + DEC DE + CP E + JP NZ, .l2 + CP D + JP NZ, .l2 + + XOR A + LD (M_VARS.esc_var0), A + POP BC + LD HL, (M_VARS.esc_param) + LD DE, 10 + ADD HL, DE +.l3: + EX DE, HL + LD HL, (M_VARS.esc_param+4) + EX DE, HL + PUSH BC + CALL fn39_sub2 + POP BC + LD A, (M_VARS.esc_var0) + ADD A, 0x2 + LD (M_VARS.esc_var0), A + CP 10 + RET Z + JP .l3 + +; --------------------------------------------------- +; Function 3C +; --------------------------------------------------- +get_image_hdr: + LD HL, (M_VARS.esc_param+4) + LD (HL), 58 ; ':' + INC HL + PUSH HL + LD HL, (M_VARS.esc_param+2) + INC L + LD C, L + LD A, H + ADD A, 0x4 + LD B, A + LD H, B + LD E, C + CALL dc_mul_e_h + ADD HL, HL + EX DE, HL + POP HL + LD (HL), E + INC HL + LD (HL), D + INC HL + LD (HL), C + INC HL + LD (HL), B + INC HL + EX DE, HL + LD HL, (M_VARS.esc_param) + ADD HL, HL + EX DE, HL + PUSH BC + PUSH HL + CALL calc_px_addr + POP DE + LD A, L + LD (DE), A + INC DE + LD A, H + LD (DE), A + INC DE + LD A, B + LD (DE), A + INC DE + POP BC + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL get_image ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; = +; --------------------------------------------------- +esc_get_put_image: + LD A, (M_VARS.esc_param+6) + CP 0x2 + JP NC, ehd_l1 + LD HL, M_VARS.esc_param + LD E, (HL) + INC HL + LD D, (HL) + INC HL + LD C, (HL) + INC HL ; TODO: next call to calc_px_addr + LD B, (HL) ; destroy value of B and HL + CALL calc_px_addr + EX DE, HL + LD HL, (M_VARS.esc_param+4) + LD A, H + CP 128 + RET C + + CP 184 + RET NC + + EX DE, HL + + ; Enable VRAM access + LD A, 0x1 + OUT (SYS_DD17PB), A + LD A, (M_VARS.esc_param+6) + OR A + JP NZ, put_image + +; --------------------------------------------------- +; Get image from VRAM to user buffer +; Inp: HL -> VRAM[x,y] +; DE -> buffer +; BC - width, height +; --------------------------------------------------- +get_image: + PUSH HL + PUSH BC +.next_row: + ; byte 1 + LD A, (HL) + LD (DE), A + INC H ; next Y (row) + INC DE + ; byte 2 ; next dst addr + LD A, (HL) + LD (DE), A + INC H + LD A, H + CP 128 ; last row? + JP NZ, .l2 + LD H, 0x40 ; reset Y +.l2: + INC DE + DEC C + JP NZ, .next_row + POP BC + POP HL + INC L + DEC B ; dec width + JP NZ, get_image + JP img_task_end + +; --------------------------------------------------- +; Put image from buffer to VRAM +; Inp: HL -> VRAM[x,y] +; DE -> buffer +; BC - width, height +; --------------------------------------------------- +put_image: + PUSH HL + PUSH BC +.next_row: + ; two bytes for 8 pixels + ; byte 1 + LD A, (DE) ; get from buffer + LD (HL), A ; put to screen + INC H ; next Y (row) + INC DE ; next src addr + ; byte 2 + LD A, (DE) + LD (HL), A + INC DE + INC H + LD A, H + CP 128 ; last row? + JP NZ, .l2 + LD H, 0x40 ; reset +.l2: + DEC C + JP NZ, .next_row + POP BC + POP HL + ; next column + INC L + DEC B + JP NZ, put_image + +img_task_end: + LD A, 0x0 + OUT (SYS_DD17PB), A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +fn39_sub2: + DEC C +.l1: + PUSH BC +.l2: + EX DE, HL + PUSH BC + PUSH HL + LD L, (HL) + LD H, 0x0 + LD A, (M_VARS.esc_var0) + LD B, A + OR A + JP Z, .l4 +.l3: + ADD HL, HL + DEC A + JP NZ, .l3 +.l4: + LD A, B + LD C, L + LD B, H + POP HL + INC HL + PUSH HL + LD L, (HL) + LD H, 0x0 + OR A + JP Z, .l6 +.l5: + ADD HL, HL + DEC A + JP NZ, .l5 +.l6: + EX DE, HL + LD A, (HL) + OR C + LD (HL), A + INC HL + LD A, (HL) + OR E + LD (HL), A + INC HL + LD (HL), B + INC HL + LD (HL), D + DEC HL + POP DE + INC DE + POP BC + DEC C + JP NZ, .l2 + POP BC + INC HL + INC HL + DEC B + JP NZ, .l1 + RET + +; -------------------------------------------------- +; Handle ASCII_CAN symbol (cursor right) +; -------------------------------------------------- +gih_rt: + DEC DE + LD A, (DE) + ADD A, 0x2 + LD (DE), A + CP 9 + RET C + LD A, 0x2 + LD (DE), A + INC DE + PUSH BC + LD H, D + LD L, E + INC HL + INC HL + LD A, (M_VARS.esc_var0) + PUSH AF + DEC A + LD (M_VARS.esc_var0), A + INC A + ADD A, A + ADD A, B + CP 128 + JP C, .l1 + SUB 0x40 +.l1: + LD B, A + LD A, (M_VARS.esc_var1) +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (HL) + LD (DE), A + INC HL + INC DE + LD A, (HL) + LD (DE), A + INC HL + INC DE + POP AF + DEC A + JP NZ, .l3 + LD A, (BC) + LD (DE), A + INC DE + INC HL + INC B + LD A, (BC) + LD (DE), A + INC HL + INC DE + DEC B + INC C + POP AF + DEC A + JP NZ, .l2 + POP AF + LD (M_VARS.esc_var0), A + POP BC + INC B + INC B + LD A, B + CP 0x80 + RET NZ + LD B, 0x40 + RET + +; -------------------------------------------------- +; Handle ASCII_BS (BackSpace) symbol +; -------------------------------------------------- +gih_bs: + DEC DE + LD A, (DE) + SUB 0x2 + LD (DE), A + RET NC + + LD A, 6 + LD (DE), A + INC DE + PUSH BC + ADD HL, DE + DEC HL + LD D, H + LD E, L + DEC HL + DEC HL + LD A, (M_VARS.esc_var1) + ADD A, C + DEC A + LD C, A + LD A, B + DEC A + CP 0x3f + JP NZ, .l1 + LD A, 0x7f ; [DEL]? +.l1: + LD B, A + LD A, (M_VARS.esc_var0) + PUSH AF + DEC A + LD (M_VARS.esc_var0), A + LD A, (M_VARS.esc_var1) +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + POP AF + DEC A + JP NZ, .l3 + LD A, (BC) + LD (DE), A + DEC HL + DEC DE + DEC B + LD A, (BC) + LD (DE), A + DEC HL + DEC DE + INC B + DEC C + POP AF + DEC A + JP NZ, .l2 + POP AF + LD (M_VARS.esc_var0), A + POP BC + DEC B + DEC B + LD A, B + CP 0x3e + RET NZ + LD B, 0x7e + RET + +; -------------------------------------------------- +; Handle ASCII_SUB symbol +; -------------------------------------------------- +gih_ctrl_z: + PUSH BC + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + ADD A, E + LD L, A + LD H, D + LD A, (M_VARS.esc_var1) + ADD A, C + LD C, A + PUSH BC + LD A, (M_VARS.esc_var1) + DEC A + DEC A + LD C, A +.l1: + LD A, (M_VARS.esc_var0) + LD B, A +.l2: + LD A, (HL) + LD (DE), A + INC HL + INC DE + LD A, (HL) + LD (DE), A + INC HL + INC DE + DEC B + JP NZ, .l2 + DEC C + JP NZ, .l1 + POP BC + LD L, 0x2 +.l3: + LD A, (M_VARS.esc_var0) + LD H, A + PUSH BC +.l4: + LD A, (BC) + LD (DE), A + INC DE + INC B + LD A, (BC) + LD (DE), A + INC DE + INC B + LD A, B + CP 0x80 + JP NZ, .l5 + LD B, 0x40 +.l5: + DEC H + JP NZ, .l4 + POP BC + INC C + DEC L + JP NZ, .l3 + POP BC + INC C + INC C + RET + +; -------------------------------------------------- +; Handle ASCII_EM symbol +; -------------------------------------------------- +gih_up: + PUSH BC + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, B + CP 128 + JP Z, .l1 + JP C, .l1 + SUB 64 +.l1: + DEC A + LD B, A + DEC C + PUSH BC + ADD HL, DE + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + LD E, A + LD A, L + SUB E + LD E, A + LD D, H + DEC DE + DEC HL + EX DE, HL + LD A, (M_VARS.esc_var1) + DEC A + DEC A + LD C, A +.l2: + LD A, (M_VARS.esc_var0) + LD B, A +.l3: + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + LD A, (HL) + LD (DE), A + DEC HL + DEC DE + DEC B + JP NZ, .l3 + DEC C + JP NZ, .l2 + POP BC + LD L, 0x2 + +.l4: + LD A, (M_VARS.esc_var0) + LD H, A + PUSH BC + +.l5: + LD A, (BC) + LD (DE), A + DEC DE + DEC B + LD A, (BC) + LD (DE), A + DEC DE + DEC B + LD A, B + CP 0x3f + JP NZ, .l6 + LD B, 0x7f + +.l6: + DEC H + JP NZ, .l5 + POP BC + DEC C + DEC L + JP NZ, .l4 + POP BC + DEC C + DEC C + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +pict_sub2: + PUSH DE + DEC DE + LD A, (DE) + LD E, A + LD D, 0x0 + LD HL, (M_VARS.esc_param+1) + ADD HL, DE + LD E, (HL) + INC HL + LD D, (HL) + POP HL + PUSH BC + PUSH HL + PUSH HL + LD HL, (M_VARS.esc_param+3) + INC HL + LD C, (HL) + INC HL + LD B, (HL) + POP HL + ADD HL, BC + LD C, L + LD B, H + POP HL + CALL mov_hl_bc + LD A, (M_VARS.esc_param+5) + OR A + JP Z, .l1 + EX DE, HL +.l1: + LD A, (M_VARS.esc_var1) + SUB 0x4 +.l2: + PUSH AF + LD A, (M_VARS.esc_var0) +.l3: + PUSH AF + LD A, (DE) + INC DE + PUSH DE + PUSH AF + LD A, (DE) + LD E, A + POP AF + LD D, A + OR E + CPL + PUSH AF + AND (HL) + OR D + LD (BC), A + INC BC + INC HL + POP AF + AND (HL) + OR E + LD (BC), A + INC BC + INC HL + POP DE + INC DE + POP AF + DEC A + JP NZ, .l3 + POP AF + DEC A + JP NZ, .l2 + LD A, (M_VARS.esc_param+5) + OR A + JP Z, .l4 + EX DE, HL +.l4: + CALL mov_hl_bc + POP DE + EX DE, HL + LD A, (M_VARS.esc_var0) + LD C, A + LD A, (M_VARS.esc_var1) + LD B, A + LD A, 0x1 + OUT (SYS_DD17PB), A + CALL put_image ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Move form [HL] to [BC] count of bytes +; Inp: HL -> src +; BC -> dst +; esc_var0*4 - count +; --------------------------------------------------- +mov_hl_bc: + PUSH DE + LD A, (M_VARS.esc_var0) + ADD A, A + ADD A, A + LD E, A ; E = param * 4 + ; move [HL] -> [BC] E bytes +.next: + LD A, (HL) + LD (BC), A + INC HL + INC BC + DEC E + JP NZ, .next + POP DE + RET + +; --------------------------------------------------- +; Draw circle +; Inp: param x,y,radius, aspect_x, aspect_y +; --------------------------------------------------- +esc_draw_circle: + LD A, (M_VARS.esc_param+2) ; radius + LD B, A + OR A + RET Z ; exit ir radius 0 + LD A, 0x7f + CP B + RET M ; exit if radius>127 + + XOR A + LD D, A ; 0 + LD E, B ; r + CALL dc_draw_8px + + LD A, 1 + LD H, A + SUB B + LD C, A + LD A, B + RLCA + LD B, A + LD A, 0x1 + SUB B + LD L, A + CCF ; TODO: unused +.l1: + INC D + LD A, E + CP D + JP Z, dc_draw_8px + CALL dc_draw_8px + LD A, H + ADD A, 0x2 + LD H, A + LD A, L + ADD A, 0x2 + LD L, A + LD A, C + ADD A, H + LD C, A + JP NC, .l1 +.l2: + CCF ; TODO: unused + INC D + DEC E + LD A, D + CP E + JP Z, dc_draw_8px + SUB E + CP 0x1 + RET Z + LD A, E + SUB D + CP 0x1 + JP Z, dc_draw_8px + CALL dc_draw_8px + LD A, H + ADD A, 0x2 + LD H, A + LD A, L + ADD A, 0x4 + LD L, A + JP NC, .l3 + CCF ; TODO: unused +.l3: + LD A, C + ADD A, L + LD C, A + JP NC, .l1 + JP .l2 + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_8px: + PUSH HL + PUSH DE + PUSH BC + PUSH DE + CALL dc_aspect_ratio_1 + LD HL, (M_VARS.esc_param) ; HL=Y,X + CALL dc_draw_4px_bc + POP DE + CALL dc_aspect_ratio2 + LD HL, (M_VARS.esc_param) ; HL=Y,X + CALL dc_draw_4px_cb + POP BC + POP DE + POP HL + XOR A + RET + +; --------------------------------------------------- +; Scale circle axis dy specified aspect ratio +; if aspect_x = 0 C = D else C = D * aspect_x / 256 +; if aspect_y = 0 B = E else B = E * aspect_y / 256 +; --------------------------------------------------- +dc_aspect_ratio_1: + LD HL, (M_VARS.esc_param+3) ; aspect_x -> L, aspect_y -> H + LD A, L + OR A + LD C, D + LD B, E + JP NZ, .dc_ax_ne0 + LD A, H + OR A + JP NZ, .dc_ay_ne0 + RET +.dc_ax_ne0: + LD A, H + LD H, L + LD E, C + CALL dc_mul_e_h + LD C, E + OR A + RET Z +.dc_ay_ne0: + LD H, A + LD E, B + CALL dc_mul_e_h + LD B, E + RET + +; --------------------------------------------------- +; if aspect_x = 0 B = E else B = E * aspect_x / 256 +; if aspect_y = 0 C = D else C = D * aspect_y / 256 +; --------------------------------------------------- +dc_aspect_ratio2: + LD HL, (M_VARS.esc_param+3) ; aspect_x -> L, aspect_y -> H + LD A, L + OR A + LD C, D + LD B, E + JP NZ, .dc_ax_ne0 + LD A, H + OR A + JP NZ, .dc_ay_ne0 + RET +.dc_ax_ne0: + LD A, H + LD H, L + LD E, B + CALL dc_mul_e_h + LD B, E + OR A + RET Z + +.dc_ay_ne0: + LD H, A + LD E, C + CALL dc_mul_e_h + LD C, E + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_mul_e_h: + LD D, 0x0 + LD L, D + ADD HL, HL + JP NC, .l1 + ADD HL, DE +.l1: + ADD HL, HL + JP NC, .l2 + ADD HL, DE +.l2: + ADD HL, HL + JP NC, .l3 + ADD HL, DE +.l3: + ADD HL, HL + JP NC, .l4 + ADD HL, DE +.l4: + ADD HL, HL + JP NC, .l5 + ADD HL, DE +.l5: + ADD HL, HL + JP NC, .l6 + ADD HL, DE +.l6: + ADD HL, HL + JP NC, .l7 + ADD HL, DE +.l7: + ADD HL, HL + JP NC, .l8 + ADD HL, DE +.l8: + LD E, H + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_4px_bc: + ; draw pixel(H+B, L+C) if in screen + LD A, H + ADD A, B + JP C, .l1 + LD D, A + LD A, L + ADD A, C + LD E, A + CALL dc_put_pixel +.l1: + ; draw pixel(H+B, L-C) if in screen + LD A, H + ADD A, B + JP C, .l2 + LD D, A + LD A, L + SUB C + LD E, A + CALL dc_put_pixel +.l2: + ; draw pixel(H-B, L-C) if in screen + LD A, H + SUB B + JP C, .l3 + LD D, A + LD A, L + SUB C + LD E, A + CALL dc_put_pixel +.l3: + ; draw pixel(H-B, L+C) if in screen + LD A, H + SUB B + RET C + LD D, A + LD A, L + ADD A, C + LD E, A + CALL dc_put_pixel ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +dc_draw_4px_cb: + ; draw pixel(H+C, L+B) if in screen + LD A, H + ADD A, C + JP C, .l1 + LD D, A + LD A, L + ADD A, B + LD E, A + CALL dc_put_pixel +.l1: + ; draw pixel(H+C, L-B) if in screen + LD A, H + ADD A, C + JP C, .l2 + LD D, A + LD A, L + SUB B + LD E, A + CALL dc_put_pixel +.l2: + ; draw pixel(H-C, L-B) if in screen + LD A, H + SUB C + JP C, l3 + LD D, A + LD A, L + SUB B + LD E, A + CALL dc_put_pixel +l3: + ; draw pixel(H-C, L+B) if in screen + LD A, H + SUB C + RET C + LD D, A + LD A, L + ADD A, B + LD E, A + CALL dc_put_pixel ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Draw pixel on screen +; Inp: DE - X, Y +; --------------------------------------------------- +dc_put_pixel: + RET C ; return if CF set (out of screen) + PUSH HL + PUSH BC + CALL calc_px_addr + ; calculate B = pixel mask + LD A, 10000000b +.roll: + RLCA ; [07654321] <- [76547210], [7] -> CF + DEC B + JP P, .roll + CPL + LD B, A + ; DE = foreground color low and hi bytes + EX DE, HL + LD HL, (M_VARS.curr_color) + EX DE, HL + ; Turn on Video RAM + LD A, 0x1 + OUT (SYS_DD17PB), A + ; Load VRAM[HL] byte (low byte), mask and set + LD A, (HL) + XOR E + AND B + XOR E + LD (HL), A + ; Load VRAM[HL+1] byte (low byte), mask and set + INC H + LD A, (HL) + XOR D + AND B + XOR D + LD (HL), A + ; Turn off Video RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + POP BC + POP HL + RET + + ; Full charset, Common + Latin letters (112*8=890) + INCLUDE "font-6x7.inc" + +; --------------------------------------------------- +; Convert 0h..Fh decimal value to symbol '0'..'F' +; --------------------------------------------------- +conv_nibble: + AND 0xf + ADD A, 0x90 + DAA + ADC A, 0x40 + DAA + LD C, A + RET + +; --------------------------------------------------- +; Print byte in HEX +; Inp: A - byte to print +; --------------------------------------------------- +m_hexb: + PUSH AF + RRCA + RRCA + RRCA + RRCA + CALL out_hex + POP AF + +out_hex: + CALL conv_nibble + CALL m_con_out ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Wtite RAM-Disk 64K to TAPE +; --------------------------------------------------- +m_tape_write_ram2: + LD HL, M_VARS.buffer + LD C, 128 +.cl_stack: + LD (HL), 0x0 + INC HL + DEC C + JP NZ, .cl_stack + LD HL, M_VARS.buffer + LD DE, 0xffff + ; write empty block + ; DE - block ID + ; HL -> block + CALL m_tape_write + CALL twr2_delay + LD DE, 0x0 + CALL m_tape_write + CALL twr2_delay + LD BC, 512 + LD DE, 0x0 +.nxt_blk: + PUSH BC + LD HL, M_VARS.buffer + CALL m_ramdisk_read + INC DE + CALL m_tape_write + CALL twr2_delay + POP BC + DEC BC + LD A, B + OR C + JP NZ, .nxt_blk + RET + +; --------------------------------------------------- +; Pause between blocks on tape +; --------------------------------------------------- +twr2_delay: + LD BC, 250 +.delay: + DEC BC + LD A, B + OR C + JP NZ, .delay + RET + +; --------------------------------------------------- +; Read RAM-Disk 64K from TAPE +; --------------------------------------------------- +m_tape_read_ram2: + LD A, 100 + CALL m_tape_wait + OR A + JP NZ, .end + LD E, 6 + +.srch_first: + DEC E + JP Z, .not_found + ; read block + LD HL, M_VARS.buffer + CALL m_tape_read + CP 4 + JP Z, .end + OR A + JP NZ, .srch_first + LD A, B + OR C + JP NZ, .srch_first + + LD BC, 512 + LD DE, 0x0 + +.rd_next: + PUSH BC + ; Read block from tape + CALL m_tape_read + OR A + JP NZ, .rd_error + DEC BC + LD A, B + CP D + JP NZ, .inv_id + LD A, C + CP E + JP NZ, .inv_id + ; Ok, write block to RAM disk + CALL m_ramdisk_write + INC DE + POP BC + DEC BC + LD A, B + OR C + JP NZ, .rd_next + RET +.not_found: + LD HL, msg_no_start_rec + CALL me_out_strz ; TODO: replace call+ret to jp + RET +.rd_error: + CP 2 + JP Z, .err_ubi + CP 4 + JP Z, .err_ibu + LD HL, msg_checksum + CALL me_out_strz + CALL out_hexw + POP BC + RET + + ; Illegal sequence of blocks +.inv_id: + LD HL, msg_sequence + CALL me_out_strz + INC BC + CALL out_hexw + POP BC + RET + +.err_ubi: + LD HL, msg_ibg + CALL me_out_strz + POP BC + RET + + ; Interrupted by user +.err_ibu: + POP BC +.end: + LD HL, msg_break + CALL me_out_strz ; TODO: replace call+ret to jp + RET + +; -------------------------------------------------- +; Output hex word +; Inp: BC - word to output +; -------------------------------------------------- +out_hexw: + PUSH BC + LD A, B + CALL m_hexb + POP BC + LD A, C + CALL m_hexb ; TODO: replace call+ret to jp + RET + +msg_no_start_rec: + DB "NO START RECORD", 0 +msg_checksum: + DB "CHECKSUM ", 0 +msg_sequence: + DB "SEQUENCE ", 0 +msg_ibg: + DB "IBG", 0 +msg_break: + DB "BREAK", 0 + +; --------------------------------------------------- +; Out ASCIIZ message +; Inp: HL -> zero ended string +; --------------------------------------------------- +me_out_strz: + LD A, (HL) + OR A + RET Z + PUSH BC + LD C, A + CALL m_con_out + INC HL + POP BC + JP me_out_strz + + + +; --------------------------------------------------- +; Read from RAM-disk to RAM +; Inp: DE - source sector +; HL -> destination buffer +; --------------------------------------------------- +m_ramdisk_read: + PUSH HL + PUSH DE + LD A, D + AND 0x1 ; Low 32K or Hi page or Ext RAM + OR 0x2 ; Set A16 address line + OR 0x0 ; TODO: nothing, remove + LD B, A ; B - value to turn on access to Ext RAM + ; Calculate DE = address from sector number + XOR A + LD A, E ; E - low address + RRA ; [CF] -> [7:0] -> [CF] + LD D, A ; D = E/2 + LD A, 0x0 + RRA ; [CF] -> E + LD E, A +.read: + ; Access to ExtRAM + LD A, B + OUT (SYS_DD17PB), A + ; Get Byte + LD A, (DE) + LD C, A + ; Access to RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + ; Set Byte + LD (HL), C + ; HL++, DE++ + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, .read ; jump if has more bytes + + ; Access to RAM + LD A, 0x0 + OUT (SYS_DD17PB), A + + POP DE + POP HL + RET + +; --------------------------------------------------- +; Write sector to RAM disk +; Inp: HL -> source buffer +; DE - destination sector +; --------------------------------------------------- +m_ramdisk_write: + PUSH HL + PUSH DE + LD A, D + AND 0x1 + OR 0x2 + OR 0x0 ; TODO: remove unused + LD B, A + XOR A + LD A, E + RRA + LD D, A + LD A, 0x0 + RRA + LD E, A +.wr_byte: + LD A, 0x0 + OUT (SYS_DD17PB), A + LD C, (HL) + LD A, B + OUT (SYS_DD17PB), A + LD A, C + LD (DE), A + INC HL + INC DE + LD A, E + ADD A, A + JP NZ, .wr_byte + LD A, 0x0 + OUT (SYS_DD17PB), A + POP DE + POP HL + RET + +; -------------------------------------------------- +; Write block to Tape +; In: DE - block ID, +; HL -> block of data. +; -------------------------------------------------- +m_tape_write: + PUSH HL + PUSH DE + PUSH DE + LD BC, 2550 + LD A, PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A,TMR0_SQWAVE ; tmr0, load lsb+msb, sq wave, bin + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A, B + OUT (TMR_DD70C1), A + ; Write Hi+Lo, Hi+Lo + LD DE, 4 ; repeat next 4 times +.l1: + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; check rst4 from timer#0 + JP NZ, .l1 + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH ; tape level hi + JP NZ, .set_lvl + LD A, TL_LOW ; tape level low +.set_lvl: + OUT (DD67PC), A ; set tape level + LD A, TMR0_SQWAVE ; tmr0, load lsb+msb, swq, bin + ; timer on + OUT (TMR_DD70CTR), A + LD A, C + OUT (TMR_DD70C1), A + LD A, B + OUT (TMR_DD70C1), A + DEC E + JP NZ, .l1 + +.l2: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .l2 + + ; Write 00 at start + LD A, 0x0 + CALL m_tape_wr_byte + ; Write 0xF5 marker + LD A, 0xf5 + CALL m_tape_wr_byte + LD E, 0x0 ; checksum=0 + ; Write block ID + POP BC + LD A, C + CALL m_tape_wr_byte + LD A, B + CALL m_tape_wr_byte + ; Write 128 data bytes + LD B, 128 +.next_byte: + LD A, (HL) + CALL m_tape_wr_byte + INC HL + DEC B + JP NZ, .next_byte + ; Write checksum + LD A, E + CALL m_tape_wr_byte + ; Write final zero byte + LD A, 0x0 + CALL m_tape_wr_byte +.wait_end: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .wait_end + LD A, TL_MID ; tape level middle + OUT (DD67PC), A + POP DE + POP HL + RET + + +; ------------------------------------------------------ +; Write byte to tape +; Inp: A - byte top write +; D - current level +; E - current checksum +; ------------------------------------------------------ +m_tape_wr_byte: + PUSH BC + ; calc checksum + LD B, A + LD A, E + SUB B + LD E, A + LD C, 8 ; 8 bit in byte +.get_bit: + LD A, B + RRA + LD B, A + JP C, .bit_hi +.wait_t: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .wait_t + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; program for 360 cycles + LD A, 0x68 + OUT (TMR_DD70C1), A + LD A, 0x1 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH + JP NZ, .out_bit + LD A, TL_LOW +.out_bit: + OUT (DD67PC), A + DEC C + JP NZ,.get_bit + POP BC + RET +.bit_hi: + IN A, (PIC_DD75RS) + AND TIMER_IRQ + JP NZ, .bit_hi + ; program for 660 cycles + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + LD A, 0x94 + OUT (TMR_DD70C1), A + LD A, 0x2 + OUT (TMR_DD70C1), A + ; change amplitude + LD A, D + CPL + LD D, A + OR A + LD A, TL_HIGH + JP NZ, .out_bit_hi + LD A, TL_LOW +.out_bit_hi: + OUT (DD67PC), A + DEC C + JP NZ, .get_bit + POP BC + RET + +; ------------------------------------------------------ +; Load block from Tape +; In: HL -> buffer to receive bytes from Tape +; Out: A = 0 - ok, +; 1 - CRC error, +; 2 - unexpected block Id +; 4 - key pressed +; ------------------------------------------------------ +m_tape_read: + PUSH HL + PUSH DE + LD A, PIC_POLL_MODE ; pool mode + OUT (PIC_DD75RS), A + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A ; tmr0, load lsb+msb, sq wave + LD A, 0x0 + ; tmr0 load 0x0000 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + LD C, 3 +.wait_3_changes: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + LD A, B + ADD A, 4 + JP P, .wait_3_changes + DEC C + JP NZ, .wait_3_changes +.wait_4th_change: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + LD A, B + ADD A, 4 + JP M, .wait_4th_change + LD C, 0x0 +.wait_f5_marker: + CALL read_tape_bit_kbd + INC A + JP Z, .key_pressed + DEC A + RRA + LD A, C + RRA + LD C, A + CP 0xf5 + JP NZ, .wait_f5_marker + LD E, 0x0 ; checksum = 0 + ; Read blk ID + CALL m_tape_read_byte + JP NC, .err_read_id + LD C, D + CALL m_tape_read_byte + JP NC, .err_read_id + LD B, D + PUSH BC + ; Read block, 128 bytes + LD C, 128 +.read_next_b: + CALL m_tape_read_byte + JP NC, .err_read_blk + LD (HL), D + INC HL + DEC C + JP NZ, .read_next_b + + ; Read checksum + CALL m_tape_read_byte + JP NC, .err_read_blk + LD A, E + OR A + JP Z, .checksum_ok + LD A, 0x1 ; bad checksum +.checksum_ok: + POP BC +.return: + POP DE + POP HL + RET + +.err_read_blk: + POP BC + LD BC, 0x0 +.err_read_id: + LD A, 0x2 ; read error + JP .return +.key_pressed: + CALL m_con_in + LD C, A ; store key code in C + LD B, 0x0 + LD A, 0x4 + JP .return + +; ------------------------------------------------------ +; Read byte from Tape +; Out: D - byte +; CF is set if ok, cleared if error +; ------------------------------------------------------ +m_tape_read_byte: + PUSH BC + LD C, 8 +.next_bit: + CALL m_read_tape_bit + ; push bit from lo to hi in D + RRA + LD A, D + RRA + LD D, A + LD A, 4 + ADD A, B + JP NC, .ret_err + DEC C + JP NZ, .next_bit + ; calc checksum + LD A, D + ADD A, E + LD E, A + SCF +.ret_err: + POP BC + RET + +; ------------------------------------------------------ +; Read bit from tape +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +m_read_tape_bit: + IN A, (KBD_DD78PB) ; Read Tape bit 5 (data) + AND TAPE_P + LD B, A +.wait_change: + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z, .wait_change + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; [360...480...660] 0x220=544d + IN A, (TMR_DD70C1) ; get tmer#0 lsb + ADD A, 0x20 + IN A, (TMR_DD70C1) ; get tmer#0 msb + LD B, A + ADC A, 0x2 + ; reset timer to 0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; For 0 - 65535-360+544 -> overflow P/V=1 + ; For 1 - 65535-660+544 -> no overflow P/V=0 + RET P + INC A + RET + +; ------------------------------------------------------ +; Read bit from tape with keyboard interruption +; Out: A - bit from tape +; B - time from last bit +; ------------------------------------------------------ +read_tape_bit_kbd: + IN A, (KBD_DD78PB) + AND TAPE_P + LD B, A ; save tape bit state + ; wait change with keyboard check +.wait_change: + IN A, (PIC_DD75RS) + AND KBD_IRQ + JP NZ, .key_pressed + IN A, (KBD_DD78PB) + AND TAPE_P + CP B + JP Z, .wait_change + ; measure time + LD A, TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; read lsb+msb + IN A, (TMR_DD70C1) + ADD A, 0x20 + IN A, (TMR_DD70C1) + LD B, A + ADC A, 0x2 + ; reset timer#0 + LD A, 0x0 + OUT (TMR_DD70C1), A + OUT (TMR_DD70C1), A + ; flag P/V is set for 0 + RET P + INC A + RET +.key_pressed: + LD A, 0xff + RET + +; ------------------------------------------------------ +; Wait tape block +; Inp: A - periods to wait +; Out: A=4 - interrupded by keyboard, C=key +; ------------------------------------------------------ +m_tape_wait: + OR A + RET Z + PUSH DE + LD B, A +.wait_t4: + LD C,B + IN A, (KBD_DD78PB) + AND TAPE_P ; Get TAPE4 (Wait det) and save + LD E, A ; store T4 state to E +.wait_next_2ms: + LD A,TMR0_SQWAVE + OUT (TMR_DD70CTR), A + ; load 3072 = 2ms + XOR A + OUT (TMR_DD70C1), A + LD A, 0xc + OUT (TMR_DD70C1), A +.wait_tmr_key: + IN A, (PIC_DD75RS) + AND KBD_IRQ ; RST1 flag (keyboard) + JP NZ, .key_pressed + IN A, (PIC_DD75RS) + AND TIMER_IRQ ; RST4 flag (timer out) + JP Z, .wait_no_rst4 + IN A, (KBD_DD78PB) + AND TAPE_P ; TAPE4 not changed? + CP E + JP NZ, .wait_t4 ; continue wait + JP .wait_tmr_key +.wait_no_rst4: + DEC C + JP NZ, .wait_next_2ms + XOR A + POP DE + RET + +.key_pressed: + CALL m_con_in + LD C, A ; C = key pressed + LD A, 0x4 ; a=4 interrupted by key + POP DE + RET + +; ------------------------------------------------------ +; Check block marker from Tape +; Out: A=0 - not detected, 0xff - detected +; ------------------------------------------------------ +m_tape_blk_detect: + IN A, (KBD_DD78PB) + AND TAPE_D ; TAPE5 - Pause detector + LD A, 0x0 + RET Z + CPL + RET + +; ====================================================== +; FDC DRIVER +; ====================================================== + + +fdc_unload_head: + LD A, 0x0 + OUT (FDC_DATA), A + LD A, FDC_RESTORE_UH_NV + OUT (FDC_CMD), A + NOP + NOP + +.wait_no_busy: + IN A, (FDC_CMD) + AND 00000101b ; Track0 , Busy + CP 00000100b + JP Z, .tr0_ok + + IN A, (FLOPPY) + RLCA ; MOTST -> CF + JP NC, .wait_no_busy + LD A, 0x20 + RET +.tr0_ok: + LD A, B + DEC A + LD A, 0x1 + JP Z, .b1 + LD (M_VARS.ul_A_var1), A + XOR A + LD (M_VARS.ul_A_var4), A + RET + +.b1: + LD (M_VARS.ul_B_var2), A + XOR A + LD (M_VARS.ul_B_var5), A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_select_drive: + PUSH AF + CALL delay_1.4mS + CP 0x1 ; TODO: DEC A to save 1b 3t + JP Z, .sel_A + LD A, 0x2 + JP .sel_B +.sel_A + LD A, 0x5 +.sel_B + LD B, A ; 010b or 101b + IN A, (FLOPPY) ; read Floppy controller reg + AND 0x40 ; SSEL + RRA ; SSEL for out + OR B ; 0x22 or 0x25 if WP + OUT (FLOPPY), A ; Select drive A or B + LD B, A + POP AF + DEC A + JP Z, .dpar_a + LD A, (bios_var2) + JP .dpar_b +.dpar_a + LD A, (BIOS.bios_var04) +.dpar_b + PUSH BC + LD B, A ; B = dpar_A or B + LD A, D ; compare with D? + CP B + JP C, .l_le ; D < B + SUB B + LD D, A + POP BC + LD A, C + OR 0x8 + LD C, A + LD A, B + AND 0x20 + OR A + RET NZ + LD A, B + OR 0x20 ; set SSEL to 1 + OUT (FLOPPY), A + CALL delay_136uS + RET +.l_le + POP BC + LD A, B + AND 0x20 + OR A + RET Z + LD A, B + AND 0x7 + OUT (FLOPPY), A + CALL delay_136uS + RET + +; --------------------------------------------------- +; Delay for 136uS +; --------------------------------------------------- +delay_136uS: + LD B, 16 ; 7 + +; --------------------------------------------------- +; Delay for B*8uS +; --------------------------------------------------- +delay_b: + DEC B ; 4 + JP NZ, delay_b ; 10 + RET ; 10 + +; --------------------------------------------------- +; Delay for 1.4mS +; --------------------------------------------------- +delay_1.4mS: + LD B, 175 ; 7 + JP delay_b ; 10 + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_read_floppy: + PUSH AF + CALL m_select_drive + POP AF + CALL m_start_seek_track + JP C, fdc_ret + CALL m_fdc_read_c_bytes + JP C, fdc_ret + XOR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_write_floppy: + PUSH AF + CALL m_select_drive + POP AF + CALL m_start_seek_track + JP C, fdc_ret + CALL m_fdc_write_bytes + JP C, fdc_ret + XOR A + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_start_seek_track: + CALL m_start_floppy + RET C + CALL m_fdc_seek_trk + RET C + RET ; TODO: remove + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_start_floppy: + LD B, A + LD A, (M_VARS.ul_var3) + CP B + JP Z, .need_m_start + CALL .wait_motor ; TODO: replace call+ret to jp + RET +.need_m_start: + IN A, (FLOPPY) + RLCA ; check MOTST + JP C, .wait_motor + IN A, (FDC_CMD) + AND FDC_NOT_READY ; not ready flag + RET Z + +; --------------------------------------------------- +; +; --------------------------------------------------- +.wait_motor: + PUSH BC + LD BC, 65535 + CALL fdc_init +.wait_rdy1: + IN A, (FDC_CMD) + AND FDC_NOT_READY + JP Z, .long_delay + IN A, (FLOPPY) + RLCA ; CF<-A[7] MOTST flag + JP NC, .wait_rdy1 + LD A, 0x20 + JP .mst_exi +.long_delay: + DEC BC + LD A, B + OR A + JP NZ, .long_delay +.mst_exi: + POP BC + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +fdc_init: + IN A, (FLOPPY) + AND 01001110b ; Get SSEL, DRSEL, MOT1, MOT0 + RRA + OUT (FLOPPY), A + OR 0x08 ; Set INIT bit + OUT (FLOPPY), A + RET + +; --------------------------------------------------- +; Seek track on floppy +; Inp: DE - track/sector +; --------------------------------------------------- +m_fdc_seek_trk: + LD A, B + DEC A + JP Z, .drv_b + LD A, (M_VARS.ul_A_var1) + OR A + CALL Z, fdc_unload_head + RET C + LD A, (M_VARS.ul_A_var4) + OUT (FDC_TRACK), A + LD A, D + LD (M_VARS.ul_A_var4), A + JP .cmn +.drv_b: + LD A, (M_VARS.ul_B_var2) + OR A + CALL Z, fdc_unload_head + RET C + LD A, (M_VARS.ul_B_var5) + OUT (FDC_TRACK), A + LD A, D + LD (M_VARS.ul_B_var5), A +.cmn: + LD A, (M_VARS.ul_var3) + CP B + LD A, B + LD (M_VARS.ul_var3), A + JP NZ, .l2 + IN A, (FDC_TRACK) + CP D + JP Z, .l2 + JP C, .l1 + LD A, (M_VARS.ul_var6) + OR A + JP NZ, .l2 + LD B, 0xff + CALL delay_b + LD A, 0x1 + LD (M_VARS.ul_var6), A + JP .l2 +.l1: + LD A, (M_VARS.ul_var6) + OR A + JP Z, .l2 + LD B, 0xff + CALL delay_b + LD A, 0x0 + LD (M_VARS.ul_var6), A +.l2: + LD A, D + OUT (FDC_DATA), A + LD A, 0x1f + OUT (FDC_CMD), A + NOP + NOP + IN A, (FDC_WAIT) + IN A, (FDC_CMD) + AND 0x19 + CP 0x0 + JP NZ, .l3 + JP .l4 +.l3: + SCF + LD A, 0x40 +.l4: + PUSH AF + LD A, E + OUT (FDC_SECT), A + POP AF + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_fdc_write_bytes: + LD A, C + OUT (FDC_CMD), A +.w_next: + IN A, (FDC_WAIT) + RRCA + LD A, (HL) + OUT (FDC_DATA), A + INC HL + JP C, .w_next + CALL fdc_check_status ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; +; --------------------------------------------------- +m_fdc_read_c_bytes: + LD A, C + OUT (FDC_CMD), A + JP .l2 +.l1: + LD (HL), A + INC HL +.l2: + IN A, (FDC_WAIT) + RRCA + IN A, (FDC_DATA) + JP C, .l1 + CALL fdc_check_status ; TODO: replace call+ret to jp + RET + +; --------------------------------------------------- +; Check fdc status for errors +; Out: CF set if errors +; --------------------------------------------------- +fdc_check_status: + IN A, (FDC_CMD) + AND 11011111b + CP 0x0 + JP Z, fdc_ret + SCF +fdc_ret: + RET + +filler1: + DB 20h + +; filler: +; ds 169, 0xff + +; ------------------------------------------------------ + +LAST EQU $ +CODE_SIZE EQU LAST-0xE000 +FILL_SIZE EQU ROM_CHIP_SIZE-CODE_SIZE + + + ASSERT m_hot_start = 0xe051 + ASSERT m_out_strz = 0xe0f1 + ASSERT m_con_out_int = 0xe16d + ASSERT get_esc_param = 0xe187 + ASSERT esc_params_tab = 0xe1cb + ASSERT esc_handler_tab = 0xe1db + ASSERT esc_set_beep = 0xe1f9 + ASSERT m_print_hor_line = 0xe23a + ASSERT m_get_7vpix = 0xe2b4 + ASSERT esc_set_palette = 0xe2fe + ASSERT m_get_glyph = 0xe320 + ASSERT m_print_no_esc = 0xe349 + ASSERT calc_addr_40 = 0xe439 + ASSERT mp_mode_64 = 0xe4b8 + ASSERT calc_addr_80 = 0xe612 + ASSERT m_clear_screen = 0xe639 + ASSERT m_cursor_home = 0xe66c + ASSERT m_draw_cursor = 0xe69a + ASSERT m_handle_esc_code = 0xe77c + ASSERT handle_cc_common = 0xe7c4 + ASSERT handle_cc_80x25 = 0xe833 + ASSERT m_beep = 0xe85a + ASSERT esc_set_cursor = 0xe890 + ASSERT esc_set_vmode = 0xe8e9 + ASSERT esc_set_color = 0xe92f + ASSERT m_print_at_xy = 0xe943 + ASSERT game_sprite_tab = 0xea39 + ASSERT esc_draw_fill_rect = 0xeb64 + ASSERT draw_line_h = 0xeed1 + ASSERT esc_draw_line = 0xef0b + ASSERT esc_draw_dot = 0xf052 + ASSERT esc_picture = 0xf0a4 + ASSERT m_fn_39 = 0xf10f + ASSERT get_image_hdr = 0xf177 + ASSERT esc_get_put_image = 0xf1b5 + ASSERT pict_sub2 = 0xf3ca + ASSERT m_font_cp0 = 0xf5bc + ASSERT me_out_strz = 0xfb36 + ASSERT m_ramdisk_write = 0xfb6d + ASSERT m_tape_write = 0xfb97 + ASSERT m_tape_wr_byte = 0xfc0e + ASSERT m_tape_read_byte = 0xfcee + ASSERT m_read_tape_bit = 0xfd08 + ASSERT m_tape_wait = 0xfd58 + + + ; DISPLAY "Code size is: ", /A, CODE_SIZE + + +FILLER + DS FILL_SIZE, 0xFF + ; DISPLAY "Free size is: ", /A, FILL_SIZE + + ENDMODULE + + OUTEND + + OUTPUT m_vars.bin + ; put in separate waste file + INCLUDE "m_vars.inc" + OUTEND diff --git a/MON_v8_c4eec374/ram.inc b/MON_v8_c4eec374/ram.inc new file mode 100644 index 0000000..8c6c08e --- /dev/null +++ b/MON_v8_c4eec374/ram.inc @@ -0,0 +1,49 @@ +; ======================================================= +; Ocean-240.2 +; +; RAM area at address: 0x0000 - 0x0100, used by CP/M and +; HW-Monitor +; By Romych 2026-02-03 +; ======================================================= + + IFNDEF _RAM + DEFINE _RAM + + MODULE RAM + +@warm_boot EQU 0x0000 ; Jump warm_boot (Restart) +@warm_boot_addr EQU 0x0001 ; address of warm boot entry point +@iobyte EQU 0x0003 ; Input/Output mapping +@cur_user_drv EQU 0x0004 ; [7:4] - curent user, [3:0] - current drive +@jp_bdos_enter EQU 0x0005 ; Jump bdos (CALL 5 to make CP/M requests) +@bdos_ent_addr EQU 0x0006 ; addres of BDOS entry point +@RST1 EQU 0x0008 +@RST1_handler_addr EQU 0x0009 +;RST2 EQU 0x0010 +;RST3 EQU 0x0018 +;RST4 EQU 0x0020 +;RST5 EQU 0x0028 +;RST6 EQU 0x0030 +;RST7 EQU 0x0038 +;reserve1 EQU 0x003b +@bios_var0 EQU 0x0040 ; 0xaa - bios init r8 +@bios_var1 EQU 0x0041 ; 0xaa - bios init r8 +@bios_var2 EQU 0x0042 ; 0x00 - bios init r8 +@bios_var3 EQU 0x0043 ; 0xff - bios init r8 +@interleave_0 EQU 0x0044 +;reserve2 EQU 0x0050 +@fcb1 EQU 0x005c ; Default FCB, 16 bytes +@fcb2 EQU 0x006c +;NMI_ISR EQU 0x0066 + +@dma_buffer EQU 0x0080 ; Default "DMA" 128 bytes buffer +@p_cmd_line_len EQU 0x0080 ; command line character count +@p_cmd_line EQU 0x0081 ; command line buffer +@fcb_ra_record_num EQU 0x00a1 +@bios_stack EQU 0x0100 +@tpa_start EQU 0x0100 ; start of program +@video_ram EQU 0x4000 + + ENDMODULE + + ENDIF \ No newline at end of file diff --git a/Test/.vscode/extensions.json b/Test/.vscode/extensions.json new file mode 100644 index 0000000..1fdc4e8 --- /dev/null +++ b/Test/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "maziac.asm-code-lens", + "maziac.dezog", + "maziac.hex-hover-converter", + "maziac.z80-instruction-set", + "maziac.sna-fileviewer" + ] +} diff --git a/Test/.vscode/launch.json b/Test/.vscode/launch.json new file mode 100644 index 0000000..c6e8ff2 --- /dev/null +++ b/Test/.vscode/launch.json @@ -0,0 +1,94 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "dezog", + "request": "launch", + "name": "Internal Simulator", + "remoteType": "zsim", + "zsim": { + "visualMemory": true, + "memoryModel": "CUSTOM", + "customMemory": { + "slots": [ + { + "name": "PAGE0", + "range": ["0x0000","0x3FFF"], + "banks": [{"index": [0, 255]}], + "initialBank": 0 + }, + { + "name": "PAGE1", + "range": ["0x4000","0x7FFF"], + "banks": [{"index": [0, 255]}], + "initialBank": 1 + }, + { + "name": "PAGE2", + "range": ["0x8000","0xBFFF"], + "banks": [{"index": [0, 255]}], + "initialBank": 2 + }, + { + "name": "PAGE3", + "range": ["0xC000","0xFFFF"], + "banks": [{"index": [0, 255]}], + "initialBank": 3 + } + ], + "ioMmu": [ + "if (portAddress == 0x82) {", + " bank = portValue;", + " PAGE0 = bank;", + "}", + "if (portAddress == 0xA2) {", + " bank = portValue;", + " PAGE1 = bank;", + "}", + "if (portAddress == 0xC2) {", + " bank = portValue;", + " PAGE2 = bank;", + "}", + "if (portAddress == 0xE2) {", + " bank = portValue;", + " PAGE3 = bank;", + "}" + ] + }, + "customCode": { + "debug": false, + "jsPath": "sim/ports.js" + //"uiPath": "simulation/ui.html" + }, + //"ulaScreen": true, + //"zxBorderWidth": 20, + //"vsyncInterrupt": true, + //"zxKeyboard": true, + //"zxBeeper": true + }, + "sjasmplus": [ + { + "path": "test_O240.sld" + } + ], + "history": { + "reverseDebugInstructionCount": 1000000, + "spotCount": 10, + "codeCoverageEnabled": true + }, + "startAutomatically": false, + "commandsAfterLaunch": [], + "rootFolder": "${workspaceFolder}", + "topOfStack": "STACK_TOP", + "loadObjs": [ + { + "path": "test_O240.obj", + "start": "0x0000" + } + ], + "execAddress": "0x8100", + "smallValuesMaximum": 513, + "tmpDir": ".tmp" + } + ] +} \ No newline at end of file diff --git a/Test/.vscode/tasks.json b/Test/.vscode/tasks.json new file mode 100644 index 0000000..7741c0c --- /dev/null +++ b/Test/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "make (sjasmplus)", + "type": "shell", + "command": "sjasmplus", + "args": [ + "--sld=test_O240.sld", + "--sym=test_O240.labels", + "--raw=test_O240.obj", + "--fullpath", + "test_O240.asm" + ], + "problemMatcher": { + "owner": "sjasmplus", + "fileLocation": "autoDetect", + "pattern": { + "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "start mame", + "type": "shell", + "command": "while true; do ./mame spectrum -window -debugger gdbstub -debug -debugger_port 12000 -verbose -resolution 512x384 ; sleep 2 ; done", + "options": { + "cwd": "${config:mame_dir}" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/Test/README.md b/Test/README.md new file mode 100644 index 0000000..604b430 --- /dev/null +++ b/Test/README.md @@ -0,0 +1,7 @@ +# Ocean-240.2 Test ROM Sources + +Source codes of TEST ROM for Ocean-240.2 computer. + +Source codes in Z80 mnemonics, but limited for i8080 instruction set. + +To compile sources, use [sjasmplus Z80 assembler](https://github.com/z00m128/sjasmplus). diff --git a/Test/test_O240.asm b/Test/test_O240.asm new file mode 100644 index 0000000..111ac6b --- /dev/null +++ b/Test/test_O240.asm @@ -0,0 +1,1249 @@ +; ======================================================= +; Исходный текст тестового ПЗУ ПК "Океан 240.2" +; Дизасемблировал Romych, 2025-08-20 +; в мнемонике ассемблера Z80 в фомате sjasmplus +; ======================================================= + + DEVICE NOSLOT64K + +; КР580ВВ55 System +DD17RA EQU 0xC0 ; VShift[8..1] +DD17RB EQU 0xC1 ; [A14,A13,REST,ENROM,A18,A17,A16,32K] +DD17RC EQU 0xC2 ; HShift[HS5..1,SB3..1] +DD17RR EQU 0xC3 ; VV55 Sys CTR + +; КР580ВВ55 LPT/Video/Tape +DD67RA EQU 0xE0 ; [LP7..0] +DD67RB EQU 0xE1 ; [VSU,C/M,FL3..1,COL3..1] +DD67RC EQU 0xE2 ; [USER3..1,STB-LP,BELL,TAPE3..1] +DD67RR EQU 0xE3 ; VV55 Video CTR + +; КР580ВИ53 +DD70C1 EQU 0x60 ; VI53 TIM/CTR1 +DD70C2 EQU 0x61 ; VI53 TIM/CTR2 +DD70C3 EQU 0x62 ; VI53 TIM/CTR3 +DD70RR EQU 0x63 ; VI53 TIM CTR + +; КР580ВВ51 +DD72RD EQU 0xA0 ; VV51 +DD72RR EQU 0xA1 ; VV51 [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] + +; КР580ВН59 +DD75RS EQU 0x80 ; VN59 +DD75RM EQU 0x81 ; VN59 + +; КР580ВВ55 Порты A и С, B5-7 - Клавиатура +DD78RA EQU 0x40 ; VV55 [Keyboard] +DD78RB EQU 0x41 ; [JST3..1,ACK,TAPE5,TAPE4,GK,GC] +DD78RC EQU 0x42 ; VV55 [PC7..0] +DD78RR EQU 0x43 ; VV55 KBD CTL + +; КР580ВВ55 Для внешних устройств пользователя +DD79RA EQU 0x00 ; VV55 User PA +DD79RB EQU 0x01 ; VV55 User PB +DD79RC EQU 0x02 ; VV55 User PC +DD79RR EQU 0x03 ; VV55 User CTR + +; КР580ВВ55 Для устройств пользователя или FDC +DD80RA EQU 0x20 ; VV55 FDC PA +DD80RB EQU 0x21 ; VV55 FDC PB +DD80RC EQU 0x22 ; VV55 FDC PC +DD80RR EQU 0x23 ; VV55 FDC CTR + +; Паттерны тестирования памяти +PATTERN1 EQU 0x55FF +PATTERN2 EQU 0x00FF +PATTERN3 EQU 0xFF00 + +; Размер блока +B32K EQU 0x8000 +B48K EQU 0xC000 + +; Задержки +DELAY1 EQU 0xFFFF ; задержка +W_BUSY EQU 0x9C40 ; таймаут ожидания готовности принтера +W_BUTN EQU 0xC350 ; таймаут отпускания клавиши S1 + +; Переменные для прерывания +T_STACK EQU 0x100 ; временный стек для прерывания +INT_CALL EQU 0x20 ; код инструкции перехода +INT_ADDR EQU 0x21 ; адрес обработчика прерывания + +; Флаги статуса ВВ51 +TX_RDY EQU 0x01 ; передатчик готов +RX_RDY EQU 0x02 ; приемник готов +; TX_EMPTY EQU 0x04 ; буфер передатчика пуст +; TX_PE EQU 0x08 ; Parity error +; TX_OE EQU 0x01 ; Overrun error +; TX_FE EQU 0x02 ; Framing error +; TX_DSR EQU 0x08 + +; Биты порта чтения с магнитофона +TAPE_4 EQU 0x04 +TAPE_5 EQU 0x08 + +; Флаги прерывания ВН59 +RST_1 EQU 0x02 +RST_2 EQU 0x04 +RST_3 EQU 0x08 + +; Бит порта строба клавиатуры +KBD_ACK EQU 0x80 + +; Флаг гашения кадра +BIT_GK EQU 0x02 ; бит гашения кадра + + +; ======================================================= +; Код теста в памяти располагается с адреса 0xE000 +; ======================================================= + + ORG 0xE000 + + JP TEST1 +; ------------------------------------------------------- +; TEST 1 Программирование портов ВВ55, Таймера ВИ53, +; Последовательного порта ВВ51 +; ------------------------------------------------------- +TEST1 + DI + ; Программирование ВВ55 + LD A, 0x80 ; 1000 0000 -> PA mode 0 out, PC out, PB mode 0 out + OUT (DD17RR), A ; mem + OUT (DD67RR), A ; video + OUT (DD80RR), A ; user + + LD A, 0x93 ; 1001 0011 -> PA mode 0 inp, PB mode 0 inp, PC hi out, PC lo inp + OUT (DD78RR), A + LD A, 0x47 ; 0100 0111 -> VSU=0, C/M=1, FL=000, COL=111 + OUT (DD67RB), A + LD A, 0x07 ; 0000 0111 -> H Shift HS=00000, SB=111 + OUT (DD17RC), A + LD A, 0x04 ; 0000 0100 -> TAPE3 = 1 + OUT (DD67RC), A + + ; Программирование порта ВВ51 + LD A, 0xCE ; 1100 1110 -> Mode: 11 - 2S, 00-N, 11-8b, 76.8КГц/16=4800 бод + OUT (DD72RR), A + LD A, 0x25 ; 0010 0101 -> 0-Norm, 0-No Rst, 1-RTS-=0, 0-No Err, 0-No brk, 1-RxEn, 0-DTR-=1, 1-TxEn + OUT (DD72RR), A + + ; Программирование таймера ВИ53 + LD A, 0x76 ; 0111 0110 -> 01-CTR2, 11-LSB+MSB, 011-square wave gen, 0-binary + OUT (DD70RR), A + LD A, 20 ; 1.536MHz/20=76.8КГц -> /16=4800 бод + OUT (DD70C2), A + LD A, 0x00 + OUT (DD70C2), A + + ; Программирование rонтроллера прерываний ВН59 + LD A, 0x12 ; ICW1 (D4=1) 0001 0010 -> D1=1 - Single mode + OUT (DD75RS), A + LD A, 0x00 ; ICW2 Interrup vector address = 0000 + OUT (DD75RM), A + LD A, 0xFF ; OCW1 1111 1111 -> Маскируем все прерывания + OUT (DD75RM), A + LD A, 0x20 ; OCW2 0010 0000 -> 001 - Non-specific EOI command, 000 - Active IR Level 0 + OUT (DD75RS), A + LD A, 0x0A ; OCW3 0000 1010 -> D3=1 Pool command, 01-No OP, RIS=0 + OUT (DD75RS), A + + LD SP, TEST1_1 ; возврат на TEST2 + LD D, 0x00 ; 0 - passed ok + LD E, 0x01 ; номер теста + JP MSG + +TEST1_1 + ; инкремент данных на пользовательском порту + LD A, D + OUT (DD80RA), A + OUT (DD80RB), A + OUT (DD80RC), A + INC D + ; проверка нажатия SA1 + IN A, (DD72RR) + AND RX_RDY + JP Z, TEST1_1 + + XOR A + OUT (DD80RA), A + OUT (DD80RB), A + OUT (DD80RC), A + + LD SP, TEST2 + JP WAIT_BTN + +; ------------------------------------------------------- +; Тест 2 Проверка работы устройства отображения +; ------------------------------------------------------- +TEST2 + ; вкл доступа к видео-ЗУ + LD A, 0x01 + OUT (DD17RB), A + ; вывод шахматки + LD C, 0x00 +T4C + LD A, C + AND 0x07 + LD B, A + LD A, C + RRA + RRA + RRA + AND 0x01 + ADD A, B + LD DE, 0x00 + RRA + JP NC, T46 + LD DE, PATTERN1 + OR A + JP Z, T46 + LD DE, PATTERN2 + CP 0x01 + JP Z, T46 + LD DE, PATTERN3 + CP 0x02 + JP Z, T46 + LD E, 0xff +T46 + LD A,C + AND 0x7 + ADD A,A + ADD A,A + ADD A,A + ADD A,A + ADD A,A + LD L,A + LD B,0x20 +T4B + LD A,C + AND 0xf8 + ADD A,0x40 + LD H,A + LD A,0x4 +T4A + LD (HL),E + INC H + LD (HL),D + INC H + DEC A + JP NZ,T4A + INC L + DEC B + JP NZ,T4B + INC C + LD A,C + CP 0x40 + JP NZ, T4C + ; нормальный маппинг ОЗУ/ПЗУ + LD A, 0x0 + OUT (DD17RB), A + + LD A, 0x40 ; 0100 000 - Color mode ON + OUT (DD67RB),A + LD E, 0x0 + LD D, 0x0 +T57 + LD BC, B48K +T4E + DEC BC + LD A,B + OR C + JP NZ,T4E + LD C, 0x80 + + ; ждем GK - гашение кадра +T2_WAIT_GK + IN A, (DD78RB) + AND BIT_GK + JP NZ, T2_WAIT_GK + +T2_WAIT_N_GK + IN A, (DD78RB) + AND BIT_GK + JP Z, T2_WAIT_N_GK + + LD A, E + RRA + JP C, T51 + INC D + JP T52 +T51 + DEC D +T52 + RRA + JP C, T53 + LD A,D + ; горизонтальный битовый сдвиг + ADD A, 0x7 + OUT (DD17RC), A + JP T54 +T53 + LD A,D + ; вертикальный сдвиг + OUT (DD17RA), A +T54 + DEC C + JP NZ, T2_WAIT_GK + LD BC, B48K +T55 + DEC BC + LD A,B + OR C + JP NZ, T55 + INC E + LD A, E + CP 0x4 + JP P, T56 + OR 0x40 ; 0100 000 + OUT (DD67RB), A + JP T57 +T56 + ; Доступ к старшим 32К доп. ОЗУ + LD A, 0x3 + OUT (DD17RB), A + LD C, 0x00 + ; шахматка в mono режиме +T5B + LD A, C + AND 0x07 + LD B, A + LD A, C + RRA + RRA + RRA + AND 0x01 + ADD A, B + LD D, 0x00 + RRA + JP NC, T58 + LD D, 0xff +T58 + LD A, C + AND 0x07 + ADD A, A + ADD A, A + ADD A, A + ADD A, A + ADD A, A + LD L, A + LD B, 0x20 +T5A + LD A, C + AND 0xF8 + ADD A, 0x40 + LD H, A + LD A, 0x04 +T59 + LD (HL), D + INC H + LD (HL), D + INC H + DEC A + JP NZ, T59 + INC L + DEC B + JP NZ, T5A + INC C + LD A,C + CP 0x40 + JP NZ, T5B + + ; Нормальный режим работы памяти + LD A, 0x00 + OUT (DD17RB),A + + LD A, 0x80 ; 1000 0000 - VSU=1 Mono + OUT (DD67RB), A + LD E, 0x00 +T5E + LD BC, DELAY1 +T2_DLY2 + DEC BC + LD A, B + OR C + JP NZ, T2_DLY2 + + LD A, E + ADD A, A + ADD A, A + ADD A, A + OR 0x80 + OUT (DD67RB), A ; VSU=1 + INC E + LD A, E + CP 0x07 + JP C, T5E + LD SP, T2_W_S2 + LD D, 0x00 ; ok + LD E, 0x02 ; test #2 + JP MSG + +T2_W_S2 + IN A, (DD72RR) + AND RX_RDY + JP Z, T2_W_S2 + + LD SP, TEST3 + JP WAIT_BTN + +;------------------------------------------------------- +; Тест3 Проверка ШД ОЗУ +; проверка шины данных ОЗУ) выполняет последовательную +; проверку ячеек ОЗУ на соответствие записываемых и +; считываемых 8-разрядных слов (для значений 00H и FFH) +;------------------------------------------------------- +TEST3 + LD A, 0xC0 ; 1100 0000 VSU=1 Color mode + OUT (DD67RB),A + LD E, 0x00 +T3_TEST_PAGE + LD A, E + ; выбор режима доступа к озу + AND 0x03 + OUT (DD17RB), A + LD HL, 0x0000 + LD BC, B32K + ; проверка записи и чтения ячейки ОЗУ +T3_TEST_CELL + LD (HL), 0x00 + LD A, (HL) + CP 0x00 + JP NZ, T3_TEST_ERR + + LD (HL), 0xFF + LD A, (HL) + CP 0xFF + JP NZ, T3_TEST_ERR + + INC HL ; addr++ + DEC BC ; counter-- + LD A, B + OR C + JP NZ, T3_TEST_CELL + ; переход к следующему банку ОЗУ + INC E + LD A, E + ; если E=4, закончим + CP 0x04 + JP M, T3_TEST_PAGE + ; тест успешен, вывод сообщения + LD D, 0x00 + LD E, 0x03 + LD SP, TEST4 + JP MSG + + ; тест ОЗУ завершен ошибкой +T3_TEST_ERR + LD A, E + AND 0x01 + RRA + RRA + ADD A, H + LD H, A + LD A, E + LD SP, T3_ERR_CONT + ; 0-осн. 1-доп ОЗУ + AND 0x02 + LD D, 0x01 + JP Z, T3_EPN3 + LD D, 0x05 +T3_EPN3 + LD E, 0x03 + JP MSG + +T3_ERR_CONT + EX DE, HL + LD A, H + AND 0x7F + LD H, A + + ; запись/чтение в ошибочную ячеку памяти и ожидание кнопки S1 +T4_WR_WS1 + LD (HL), 0x00 + LD A, (HL) + LD (HL), 0xFF + LD A, (HL) + IN A, (DD72RR) + AND RX_RDY + JP Z, T4_WR_WS1 + + ; обычный режим памяти + LD A, 0x00 + OUT (DD17RB), A + JP T4_L1 + +;------------------------------------------------------- +; Тест 4 Проверка адресов и регенерации ОЗУ +;------------------------------------------------------- +TEST4 + + ; обычный режим памяти + LD A, 0x00 + OUT (DD17RB), A +T4_WS1 + IN A, (DD72RR) + AND RX_RDY + JP Z, T4_WS1 + +T4_L1 + LD SP, T4_L2 + JP WAIT_BTN + +T4_L2 + LD E, 0x0 + +T4_W_PG + LD A, E + ; выбор страницы ОЗУ + AND 0x03 + OUT (DD17RB), A + LD HL, 0x0000 + LD BC, B32K + +T4_W_FN + ; value = (Addr AND 0FFH) + (Addr/8) + LD A, E + AND 0x01 + RRA + RRA + ADD A, H + ADD A, L + LD (HL), A + INC HL ; addr++ + DEC BC ; ctr++ + LD A,B + OR C + JP NZ,T4_W_FN + + INC E + LD A,E + CP 0x4 + JP M, T4_W_PG + + ; ожидание 0,5 сек + LD BC,DELAY1 +T4_WS2 + DEC BC + LD A, B + OR C + JP NZ, T4_WS2 + + ; чтение записанного, после паузы, проверка refresh + LD E, 0x00 +T4_R_PG + LD A, E + AND 0x03 + ; выбор страниц ОЗУ + OUT (DD17RB), A + LD HL, 0x0000 + LD BC, B32K +T4_R_FN + LD A, E + AND 0x01 + RRA + RRA + ADD A,H + ADD A,L + ; прочитали то, что писали? + CP (HL) + JP NZ, T4_R_ERR + INC HL ; addr++ + DEC BC ; ctr-- + LD A,B + OR C + JP NZ, T4_R_FN + + INC E + LD A,E + CP 0x04 + JP M,T4_R_PG + + ; тест завершен, нормальноая адресация ОЗУ + LD A, 0x00 + OUT (DD17RB),A + ; вывод результата теста 4 ok + LD D, 0x00 + LD E, 0x04 + LD SP, TEST5 + JP MSG + + ; вывод ошибки теста 4 +T4_R_ERR + ; установка флага осн/доп ОЗУ + LD A, E + AND 0x01 + RRA + RRA + ADD A, H + LD H, A + LD A, E + LD SP, TEST5 + AND 0x02 + LD D, 0x01 + JP Z, T4_EPN3 + LD D, 0x05 +T4_EPN3 + LD E, 0x04 + JP MSG + +;------------------------------------------------------- +; Тест 5 Запись на кассетный магнитофон тестового +; сигнала +;------------------------------------------------------- +TEST5 + ; обычный режим памяти + LD A, 0x0 + OUT (DD17RB), A + +T5_W_S1 + IN A, (DD72RR) + AND RX_RDY + JP Z, T5_W_S1 + + LD SP, T5_L1 + JP WAIT_BTN + +T5_L1 + LD B, 0x04 +T5_L2 + LD C, 0x06 + LD DE, T5_L5 +T5_L3 + LD A, (DE) +T5_L4 + ADD HL, HL + ADD HL, HL + DEC H + NOP + NOP + DEC A + JP NZ, T5_L4 + + LD A, B + XOR 0x02 + LD B, A + ; вывод данных на ленту + OUT (DD67RC), A + INC DE + DEC C + JP NZ, T5_L3 + IN A, (DD72RR) + AND 0x2 + JP Z, T5_L2 + LD D, 0x0 + LD E, 0x5 + LD SP, TEST6 + JP MSG + +T5_L5 + DB 0x0F, 0x07, 0x0F, 0x0B, 0x07, 0x44 + +;------------------------------------------------------- +; Тест 6 проверка правильности настройки усилителя- +; формирователя (УФ) считывания +;------------------------------------------------------- +TEST6 + LD SP, T6_L1 + JP WAIT_BTN + + ; ожидание паузы при чтении с ленты +T6_L1 + IN A, (DD78RB) + AND TAPE_4 + LD B, A +T6_L2 + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L2 + LD B, A +T6_INT_NXT + LD C, 0x00 +T6_L4 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END ; лишнее + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L4 + LD B, A + LD A, C + CP 0x19 + JP C, T6_INT_NXT + LD C, 0x00 + +T6_L5 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L5 + + LD B,A + LD A,C + CP 0x09 + JP C, T6_INT_ERR + CP 0x09 + JP C, T6_INT_ERR + LD C, 0x00 + +T6_L6 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L6 + + LD B, A + LD A, C + CP 0x07 + JP NC, T6_INT_ERR + CP 0x07 + JP NC, T6_INT_ERR + LD C, 0x00 + +T6_L7 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L7 + + LD B, A + LD A, C + CP 0x09 + JP C, T6_INT_ERR + CP 0x09 + JP C, T6_INT_ERR + LD C, 0x00 + +T6_L9 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L9 + LD B, A + LD A, C + CP 0x09 + JP NC, T6_INT_ERR + CP 0x07 + JP C, T6_INT_ERR + LD C, 0x00 + +T6_L10 + INC C + IN A, (DD72RR) + AND RX_RDY + JP NZ, T6_END + JP NZ, T6_END + IN A, (DD78RB) + AND TAPE_4 + CP B + JP Z, T6_L10 + LD B, A + LD A, C + CP 0x07 + JP NC, T6_INT_ERR + LD E, '+' + JP T6_W_TX + + ; вывод '-' при несоответствии интервала +T6_INT_ERR + LD E, '-' + + ; ждем готовности передатчика и выводим +T6_W_TX + IN A, (DD72RR) + AND TX_RDY + JP Z, T6_W_TX + LD A, E + OUT (DD72RD), A + + CP 0x2B + JP Z, T6_L12 + ; импульс 5мс на РА0 DD80 + LD A, 0x01 + OUT (DD80RA), A + XOR A + OUT (DD80RA), A + +T6_L12 + IN A, (DD72RR) + AND RX_RDY + JP Z, T6_INT_NXT + +T6_END + ; тест 6 ок + LD D, 0x00 + LD E, 0x06 + LD SP, TEST7 + JP MSG + +;------------------------------------------------------- +; Тест 7 ввод с клавиатуры 7-разрядных кодов символов +; и передача их на терминал +;------------------------------------------------------- +TEST7 + LD SP, T7_W_STB + JP WAIT_BTN + ; ждем сигнала запроса прерывания от клавиатуры (STB-) +T7_W_STB + IN A, (DD75RS) + AND RST_1 + JP NZ, T7_KBD_RQ + + IN A, (DD72RR) + AND RX_RDY + JP Z, T7_W_STB + ; Выход из теста по кнопке S1 с сообшением об успехе + LD D, 0x0 + LD E, 0x7 + LD SP, TEST8 + JP MSG + + ; обработка клавиши +T7_KBD_RQ + ; чтение кода клавиши + IN A, (DD78RA) + ; код клавиши в порт PA DD80 + OUT (DD80RA), A + LD B, A + LD A, KBD_ACK + ; подтверждение чтения клавиатуры сигналом ACK + OUT (DD78RC), A + ; ожидаем реакции клавиатуры +T7_W_STB1 + IN A, (DD75RS) + AND RST_1 + JP NZ, T7_W_STB1 + ; убираем сигнал ACK + XOR A + OUT (DD78RC), A + ; ждем готовности передатчика +T7_W_TXR + IN A, (DD72RR) + AND TX_RDY + JP Z, T7_W_TXR + ; вывод кода клавиши в последовательный порт + LD A, B + OUT (DD72RD), A + JP T7_W_STB + +;------------------------------------------------------- +; Тест 8 Проверки контроллера прерываний +; и системного таймера +;------------------------------------------------------- +TEST8 + LD E, 4 + ; ждем 2с +T8_W2 + LD BC, DELAY1 +T8_W05 + DEC BC + LD A, B + OR C + JP NZ, T8_W05 + DEC E + JP NZ, T8_W2 + + ; программируем таймер + LD A, 0x36 ; 0011 0110 -> 00 - канал , 11 - запись слова, 011 - режим 3, 0 - двоичный счет + OUT (DD70RR), A + ; сброс счетчика + XOR A + OUT (DD70C1), A + OUT (DD70C1), A + + LD SP, T_STACK + LD A, 0xC3 ; CALL xxxx + LD (INT_CALL), A + LD HL, T9_INT_HNDL + LD (INT_ADDR), HL + + ; программируем контроллер на прерывание от таймера + LD A, 0xEF ; 1110 1111 -> вкл RST4 + OUT (DD75RM), A + LD A,0x20 + OUT (DD75RS), A + + ; пауза на ловлю прерывания + LD DE, DELAY1 + EI +T9_W_IRQ + DEC DE + LD A, D + OR E + JP NZ, T9_W_IRQ + + ; не поймали, ошибка и переход на тест 9 + DI + LD E, 0x08 + LD D, 0x02 + LD SP, TEST9 + JP MSG + +T9_INT_HNDL + DI + ; поймали прерывание без ошибок + LD E, 0x08 + LD D, 0x00 + LD SP,TEST9 + JP MSG + +;------------------------------------------------------- +; Тест 9 Проверка печати на принтер +;------------------------------------------------------- +TEST9 + ; ждем кнопку S1 + LD SP, T9_W_S1 + JP WAIT_BTN +T9_W_S1 + IN A,(DD72RR) + AND RX_RDY + JP Z,T9_W_S1 + + LD SP, T9_L1 + JP WAIT_BTN +T9_L1 + + LD C, 0x20 ; ' ' +T9_LE_127 + LD DE, W_BUSY ; 9C40 + + ; статус сигнала BUSY от принтера +T9_W_BUSY + IN A, (DD75RS) + AND RST_3 + JP NZ, T9_STROBE_ON + + DEC DE + LD A,D + OR E + JP NZ, T9_W_BUSY + ; принтер не освободился + JP T9_T_OUT + + ; выдаем строб +T9_STROBE_ON + LD A, C + OUT (DD67RA), A ; вывод символа + LD A, 0x14 ; 0001 0100 STROBE + TAPE3 + OUT (DD67RC), A + ; ждем реакции принтера + LD DE, W_BUSY +T9_W_BUSY1 + IN A,(DD75RS) + AND RST_3 + + JP Z, T9_STROBE_OFF + DEC DE + LD A,D + OR E + JP NZ,T9_W_BUSY1 + JP T9_T_OUT + ; убираем строб +T9_STROBE_OFF + LD A, 0x04 ; 0000 0100 + OUT (DD67RC), A + +T9_T_OUT + IN A, (DD72RR) + AND RX_RDY + JP NZ, T9_S1 + INC C ; следующий код символа + LD A, C + CP 127 ; печатные символы с кодом <127 + JP C, T9_LE_127 + LD C, ' ' + JP T9_LE_127 + + ; выход из теста по клавише S1 +T9_S1 + LD E, 0x09 + LD D, 0x00 + LD SP, TEST_END + JP MSG + +;------------------------------------------------------- +; Окончание тестов +;------------------------------------------------------- +TEST_END + ; вывод сообщения об окончании в последовательный порт + LD HL, MS4 +TE_OUT_CHAR + ; проверка конца строки + LD A, (HL) + OR A + JP Z, TE_STOP + + LD B, A + ; ждем готовности передатчика +TE_W_S1 + IN A, (DD72RR) + AND TX_RDY + JP Z, TE_W_S1 + ; передаем байт в последовательный порт + LD A, B + OUT (DD72RD),A + INC HL ; addr++ + JP TE_OUT_CHAR +TE_STOP + JP TE_STOP + +;------------------------------------------------------- +; Ожидание отпускания кнопки S1 +;------------------------------------------------------- +WAIT_BTN + ; очистить буфер приема + IN A, (DD72RD) + IN A, (DD72RD) + ; прочитать статус + IN A, (DD72RR) + ; пока нажата S1 ждем + AND RX_RDY + JP NZ, WAIT_BTN + LD BC, W_BUTN +WB_DELAY + DEC BC + LD A,B + OR C + JP NZ,WB_DELAY + ; очистить буфер приема + IN A, (DD72RD) + IN A, (DD72RD) + LD HL, 0x0000 + ; возврат к точке перехода + ADD HL, SP + JP (HL) + +;------------------------------------------------------- +; Вывод сообщения +; Inp: D - результат теста (0-PASSED, 1-ERROR AT, 2-ERROR) +; E - номер теста +; HL - адрес +;------------------------------------------------------- +MSG + LD C, E ; число гудков + ; ------ beep +MSG_RPT0 + LD B, 125 +MSG_RPT1 + LD A, D + OR A + LD A, 0x3C + JP Z, MSG_DLY1 + LD A, 120 +MSG_DLY1 + DEC A + JP NZ, MSG_DLY1 + LD A, 0x0C ; 0000 1100 [STB,BELL]=00 [TAPE3..2]=11 + OUT (DD67RC), A + LD A, D + OR A + LD A, 0x3C ; 0011 1100 [STB,BELL]=11 [TAPE3..2]=11 + JP Z, MSG_DLY2 + LD A, 120 +MSG_DLY2 + DEC A + JP NZ, MSG_DLY2 + LD A, 0x04 ; 0000 0100 [TAPE3]=1 + OUT (DD67RC), A + DEC B + JP NZ, MSG_RPT1 + ; пауза после гудка 50*200 раз + LD B, 50 +MSG_DLY3 + LD A, 200 +MSG_DLY4 + DEC A + JP NZ, MSG_DLY4 + DEC B + JP NZ, MSG_DLY3 + DEC C + JP NZ, MSG_RPT0 + ; вывод строки в последовательный порт + LD BC, MSG_TEST +MSG_SEND_CHAR + LD A, (BC) + OR A + JP Z, MSG_TEST_END + ; ждем готовности ВВ51 +MSG_W_TX_EN1 + IN A,(DD72RR) + AND TX_RDY + JP Z,MSG_W_TX_EN1 + ; передача символа + LD A,(BC) + OUT (DD72RD),A + INC BC + JP MSG_SEND_CHAR + ; Ожидание конца передачи +MSG_TEST_END + IN A, (DD72RR) + AND TX_RDY + JP Z, MSG_TEST_END + + ; номер теста в строку и передача + LD A, E + AND 0x0F + ADD A, 0x30 + OUT (DD72RD), A + + ; вывод результата теста + LD BC, MSG_PASS + LD A, D + OR A + JP Z, MSG_OUT_RES + LD BC, MSG_ERR_AT + RRA + JP C, MSG_OUT_RES + LD BC, MSG_ERR +MSG_OUT_RES + LD A, (BC) + OR A + JP Z, MSG_ERR_END + ; посимвольная отпр сообщения об ошибке +MSG_NXT_ERR + IN A,(DD72RR) + AND TX_RDY + JP Z, MSG_NXT_ERR + LD A, (BC) + OUT (DD72RD), A + INC BC + JP MSG_OUT_RES +MSG_ERR_END + ; отправка нулей в порты пользователя/FDC + XOR A + OUT (DD80RA),A + OUT (DD80RB),A + OUT (DD80RC),A + LD A, D + ; надо выводить адрес? + AND 0x01 + JP Z, MSG_EXIT + LD A, D + RRA + RRA + AND 0x01 + ; PC0 <- 0/1 - осн/доп ОЗУ + OUT (DD80RC),A + ADD A, 0x30 + LD B, A +MSG_W_TX_EN2 + IN A,(DD72RR) + AND TX_RDY + JP Z,MSG_W_TX_EN2 + + LD A, B + OUT (DD72RD), A + LD B, 4 ; addr len 4 bytes + ; вывод адреса ошибки на порт пользователя A и B + LD A, L + OUT (DD80RA),A + LD A, H + OUT (DD80RB),A + LD D, H + LD E, L + + ; вывод адреса в HEX +MSG_TO_HEX + LD A, H + RRA + RRA + RRA + RRA + AND 0x0F + ADD A, 0x90 + DAA + ADC A, 0x40 + DAA + LD C,A + ; вывод HEX символа адреса +MSG_W_TX_EN3 + IN A,(DD72RR) + AND 0x1 + JP Z, MSG_W_TX_EN3 + LD A, C + OUT (DD72RD),A + + LD C, 0x4 +MSG_A_SR4 + LD A, L + RLA + LD L, A + LD A, H + RLA + LD H, A + DEC C + JP NZ, MSG_A_SR4 + ; вывод следующего символа адреса, если не закончили + DEC B + JP NZ, MSG_TO_HEX + ; возврат назад к точке указанной в SP +MSG_EXIT + LD HL, 0x0000 + ADD HL, SP + JP (HL) + +; ------------------------------------------------------- +; Сообщения +; ------------------------------------------------------- +MSG_TEST + DB "\r\nTEST ", 0 + +MSG_PASS + DB " PASSED", 0 + +MSG_ERR + DB " ERROR!", 0 + +MSG_ERR_AT + DB " ERROR AT ADDR ", 0 + +MS4 + DB "\r\nEND", 0 + DB 0x22 + +; ------------------------------------------------------- +; Заполнение остатака ПЗУ байтами FF до 2КБ +; ------------------------------------------------------- +LAST EQU $ +CODE_SIZE EQU LAST-0xE000 +FILL_SIZE EQU 2048-CODE_SIZE + + DISPLAY "Code size is: ",/A,CODE_SIZE + +FILLER + DS 2048-CODE_SIZE, 0xFF + DISPLAY "Filler size is: ",/A,FILL_SIZE + + END \ No newline at end of file