edlin/edlin.asm
2021-08-01 17:27:44 +03:00

1414 lines
22 KiB
NASM

cpu 8086
org 0x100
; FCB defines
fcb_filename equ 1
fcb_extension equ fcb_filename + 8
fcb_record_size equ 14
fcb_rename_target equ 17
fcb_record_low equ 33
fcb_record_high equ 35
; PSP defines
psp_segment_size equ 0x0006
psp_fcb_1 equ 0x005c
; Syscall defines
sys_read_char equ 1
sys_print_char equ 2
sys_print_string equ 9
sys_read_line equ 0xa
sys_open_file equ 0xf
sys_close_file equ 0x10
sys_delete_file equ 0x13
sys_create_file equ 0x16
sys_rename_file equ 0x17
sys_set_dta equ 0x1a
sys_set_interrupt equ 0x25
sys_random_block_read equ 0x27
sys_random_block_write equ 0x28
; Interrupt defines
int_ctrl_break equ 0x23
; Special instruction encodings
%define r_ax 0
%define r_cx 1
%define r_dx 2
%define r_bx 3
%define r_sp 4
%define r_bp 5
%define r_si 6
%define r_di 7
%define r_al 0
%define r_cl 1
%define r_dl 2
%define r_bl 3
%define r_ah 4
%define r_ch 5
%define r_dh 6
%define r_bh 7
%define b_bx_si 0
%define b_bx_di 1
%define b_bp_si 2
%define b_bp_di 3
%define b_si 4
%define b_di 5
%define b_bp 6
%define b_bx 7
; Direction swapped reg, reg
%macro addw 2
db 0x03, 0xc0 + 8 * %1 + %2
%endmacro
%macro adcb 2
db 0x12, 0xc0 + 8 * %1 + %2
%endmacro
%macro cmpw 2
db 0x3b, 0xc0 + 8 * %1 + %2
%endmacro
%macro cmpb 2
db 0x3a, 0xc0 + 8 * %1 + %2
%endmacro
%macro movw 2
db 0x8b, 0xc0 + 8 * %1 + %2
%endmacro
%macro movb 2
db 0x8a, 0xc0 + 8 * %1 + %2
%endmacro
%macro orw 2
db 0x0b, 0xc0 + 8 * %1 + %2
%endmacro
%macro orb 2
db 0x0a, 0xc0 + 8 * %1 + %2
%endmacro
%macro subw 2
db 0x2b, 0xc0 + 8 * %1 + %2
%endmacro
%macro subb 2
db 0x2a, 0xc0 + 8 * %1 + %2
%endmacro
%macro xorw 2
db 0x33, 0xc0 + 8 * %1 + %2
%endmacro
%macro xorb 2
db 0x32, 0xc0 + 8 * %1 + %2
%endmacro
; Sign extended byte to byte
%macro cmpb_addr_ext 2
db 0x82, 0x3e ; cmp byte […], byte +… (extended)
dw %1
db %2
%endmacro
%macro cmpb_regind_ext 2
db 0x82, 0x38 + %1
db %2
%endmacro
%macro cmpb_ext 2
db 0x82, 0xf8 + %1
db %2
%endmacro
jmp entrypoint
; This is never used by the program
db 13, 10, "The IBM Personal Computer EDITOR", 13, 10
db "Version 1.00 (C)Copyright IBM Corp 1981", 13, 10, '$'
db "Licensed Material - Program Property of IBM"
print_filename_missing_error: ; 017b
mov dx, filename_missing_error
_trampoline_error_1:
jmp error
entrypoint: ; 0181
mov byte [__0a7f], 0
mov sp, stack.end
ensure_file_argument:
cmpb_addr_ext psp_fcb_1 + fcb_filename, ' '
je print_filename_missing_error
; AL at program start a flag of whether drive specifier in first parameter valid
orb r_al, r_al
mov dx, invalid_name_error
jnz _trampoline_error_1
mov si, bak_extension
mov di, psp_fcb_1 + fcb_filename + 8
mov cx, 3
repe cmpsb
je print_bak_error
open_file:
mov ah, sys_open_file
mov dx, psp_fcb_1
int 0x21
mov [new_file_flag], al
orb r_al, r_al
jz delete_old_bak
mov dx, new_file_str
mov ah, sys_print_string
int 0x21
delete_old_bak: ; 01b9
; Borrow the FCB we'll be later using to create the new file with
; Since we are done with the old .BAK before we start using it for
; its main purpose, this is safe
mov si, psp_fcb_1
mov di, new_file_fcb
mov cx, 9
rep movsb
mov si, bak_extension
movsw
movsb
mov ah, sys_delete_file
mov dx, new_file_fcb
int 0x21
create_new_file:
; Create the new file we'll be writing to. EDLIN never writes to
; the file we opened for reading, but instead renames that one to
; .BAK and then writes out the changed contents to a new file
;
; The file is called .$$$ until we either exit without saving in
; which case it's deleted, or with saving, in which case it's
; renamed to the original extension
mov al, '$'
mov di, new_file_fcb + fcb_extension
stosb
stosb
stosb
mov ah, sys_create_file
int 0x21
orb r_al, r_al
jz setup_file_parameters
mov dx, directory_full_error
jmp error
print_bak_error: ; 01e6
mov dx, bak_error
jmp error
setup_file_parameters: ; 01ec
xorw r_ax, r_ax
mov [psp_fcb_1 + fcb_record_low], ax
mov [psp_fcb_1 + fcb_record_high], ax
mov [new_file_fcb + fcb_record_low], ax
mov [new_file_fcb + fcb_record_high], ax
inc ax
mov [psp_fcb_1 + fcb_record_size], ax
mov [new_file_fcb + fcb_record_size], ax
mov dx, file_buffer
movw r_di, r_dx
mov ah, sys_set_dta
int 0x21
mov cx, [psp_segment_size]
dec cx
mov [last_valid_address], cx
test byte [new_file_flag], 0xff
jnz initialize_editor
sub cx, file_buffer
; cx is now the amount of memory available starting at file_buffer
; __0a94 = ¼ available memory
shr cx, 1
movw r_ax, r_cx
shr cx, 1
mov [__0a94], cx
; cx = ¾ available memory
addw r_cx, r_ax
movw r_dx, r_cx
add dx, file_buffer
; File buffer is actually 1 byte more than we load from the file
; This is because we always place a ^Z after file contents
mov [last_file_buffer_byte], dx
read_file:
mov dx, psp_fcb_1
mov ah, sys_random_block_read
int 0x21
call find_file_end_char
addw r_di, r_cx
initialize_editor: ; 0240
cld
; Place ^Z after the end of file in memory
mov byte [di], 0x1a ; ^Z
mov [last_file_byte], di
mov byte [input_buffer.size], input_buffer.bufend - input_buffer.bufstart
mov byte [__0c1e.size], 0xff
mov byte [__0d48], 0x0a ; magic
mov word [__0a92], file_buffer
mov word [current_line], 1
mov word [command_linenum], 1
test byte [new_file_flag], 0xff
jnz editor_mainloop
call command_a
editor_mainloop: ; 0273
mov sp, stack.end
mov ax, sys_set_interrupt * 0x100 + int_ctrl_break
mov dx, ctrl_break_handler
int 0x21
mov al, '*'
call print_char
mov dx, input_buffer
mov ah, sys_read_line
int 0x21
mov al, 10 ; LF
call print_char
mov word [__0a82], 0
mov byte [__0a7d], 0
mov si, input_buffer.bufstart
call parse_line_specifier
mov [command_linenum], dx
call skip_spaces.no_load
cmp al, ','
jne .no_skip_comma
inc si
.no_skip_comma:
dec si
call parse_line_specifier
mov [__0a82], dx
call skip_spaces.no_load
cmp al, '?'
jne .not_question_mark
mov [__0a7d], al
call skip_spaces
.not_question_mark:
cmp al, 0x5f
jna .match_command
and al, 0x5f
.match_command:
mov di, commands
mov cx, 10
repne scasb
jne print_entry_error ; Not found
; CX counts down, so first entry matching gives cx=9, second cx=8, etc.
movw r_bx, r_cx
mov ax, [__0a82]
orw r_ax, r_ax
jz __02e0
cmp ax, [command_linenum]
jb print_entry_error
__02e0:
shl bx, 1
call [bx + command_addresses]
jmp near editor_mainloop
skip_spaces: ; 02e9
lodsb
.no_load: ; 02ea
cmp al, ' '
je skip_spaces
.ret: ret
print_entry_error: ; 02ef
mov dx, entry_error
mov ah, sys_print_string
int 0x21
jmp editor_mainloop
parse_line_specifier: ; 02f9
call skip_spaces
cmp al, '.'
je .current_line
cmp al, '#'
je .last_line
mov dx, 0
mov cl, 0 ; No digits read yet
.loop: ; 0309
cmp al, '0'
jb .not_digit
cmp al, '9'
ja .not_digit
; Would we overflow?
cmp dx, 65536 / 10
jnb print_entry_error ; Yes
mov cl, 1 ; We have now read a digit
sub al, '0'
; dx = dx*10
movw r_bx, r_dx
shl dx, 1
shl dx, 1
addw r_dx, r_bx
shl dx, 1
cbw
addw r_dx, r_ax
lodsb
jmp .loop
.not_digit: ; 032b
; Have we started reading a number?
cmpb_ext r_cl, 0
je skip_spaces.ret ; No
; Yes, and it's zero
orw r_dx, r_dx
jz print_entry_error
; Yes, it's nonzero
ret
.current_line: ; 0335
mov dx, [current_line]
lodsb
ret
.last_line: ; 033b
mov dx, 0xfffe ; TODO: Why 0xfffe and not 0xffff?
lodsb
ret
commands db 'QWASRDLIE', 13 ; 0340
; This is reversed in regards to commands table due to implementation of matching
command_addresses: ; 034a
dw command_cr ; CR
dw command_e ; E
dw command_i ; I
dw command_l ; L
dw command_d ; D
dw command_r ; R
dw command_s ; S
dw command_a ; A
dw command_w ; W
dw command_q ; Q
; in:
; di = buffer
; cx = size of buffer
; out:
; cx = size of buffer upto and including the ^Z, or original passed size
; equals (zero) flag = was there a ^Z
find_file_end_char: ; 035e
push di
push cx
mov al, 0x1a ; ^Z
repne scasb
movw r_di, r_cx
pop cx
lahf
subw r_cx, r_di
sahf
pop di
.ret: ret
_trampoline_print_eof_str_1: ; 036d
jmp print_eof_str
command_a: ; 0370
test byte [new_file_flag], 0xff
jnz _trampoline_print_eof_str_1
mov dx, [last_file_byte]
cmp word [command_linenum], 0
jnz __0388
cmp dx, [last_file_buffer_byte]
jnb find_file_end_char.ret
__0388:
movw r_di, r_dx
mov ah, sys_set_dta
int 0x21
mov cx, [last_valid_address]
subw r_cx, r_dx
jz _trampoline_oom_1
mov dx, psp_fcb_1
mov ah, sys_random_block_read
int 0x21
mov [new_file_flag], al
push cx
call find_file_end_char
jne __03ab ; If the ^Z was not found
mov byte [new_file_flag], 1 ; magic
__03ab:
xorw r_dx, r_dx
mov bx, [command_linenum]
orw r_bx, r_bx
jnz __03ca
movw r_ax, r_di
addw r_ax, r_cx
cmp ax, [last_file_buffer_byte]
jna __03ca
mov di, [last_file_buffer_byte]
movw r_cx, r_ax
subw r_cx, r_di
mov bx, 1
__03ca:
call __04a4
cmp [di - 1], al
je __03de
std
dec di
; TODO: Why are we using last valid address as our loop counter?
mov cx, [last_valid_address]
repne scasb
inc di
inc di
dec dx
cld
__03de:
pop cx
mov word [di], 0x1a ; ^Z
subw r_cx, r_di
xchg di, [last_file_byte]
addw r_di, r_cx
sub [psp_fcb_1 + fcb_record_low], di
sbb word [psp_fcb_1 + fcb_record_high], 0
cmpw r_bx, r_dx
jne __0406
mov byte [new_file_flag], 0 ; magic
ret
print_eof_str: ; 03fe
mov dx, eof_str
mov ah, sys_print_string
int 0x21
.ret: ret
__0406:
test byte [new_file_flag], 0xff
jnz print_eof_str
test byte [__0a7f], 0xff
jnz print_eof_str.ret
_trampoline_oom_1: ; 0414
jmp oom
command_w: ; 0417
mov bx, [command_linenum]
orw r_bx, r_bx
jnz __043b
mov cx, [__0a94]
mov di, [last_file_byte]
subw r_di, r_cx
jna print_eof_str.ret
cmp di, file_buffer
jna print_eof_str.ret
xorw r_dx, r_dx
mov bx, 1 ; magic
call __04a4
jmp __043f
__043b:
inc bx
call __0482
__043f:
movw r_cx, r_di
mov dx, file_buffer
subw r_cx, r_dx
je print_eof_str.ret
mov ah, sys_set_dta
int 0x21
mov dx, new_file_fcb
mov ah, sys_random_block_write
int 0x21
orb r_al, r_al
jnz disk_full
movw r_si, r_di
mov di, file_buffer
mov [__0a92], di
mov cx, [last_file_byte]
subw r_cx, r_si
inc cx
rep movsb
dec di
mov [last_file_byte], di
mov word [current_line], 1
__0474:
ret
disk_full: ; 0475
mov ah, sys_close_file
int 0x21
mov dx, disk_full_error
error: ; 047c
mov ah, sys_print_string
int 0x21
int 0x20
__0482:
mov dx, [current_line]
mov di, [__0a92]
cmpw r_bx, r_dx
je __0474
ja __049e
orw r_bx, r_bx
jz __049e
mov dx, 1 ; magic
mov di, file_buffer
cmpw r_bx, r_dx
je __0474
__049e:
mov cx, [last_file_byte]
subw r_cx, r_di
__04a4:
mov al, 10 ; magic
orb r_al, r_al
__04a8:
jcxz __0474
repne scasb
inc dx
cmpw r_bx, r_dx
jnz __04a8
__04b1:
ret
print_line_prefix: ; 04b2
; Line number in bx
push bx
mov al, ' '
call print_char
call print_number
mov al, ':'
call print_char
mov al, '*'
pop bx
cmp bx, [current_line]
je .end
mov al, ' '
.end: jmp print_char
print_number: ; 04ce
; Input in bx
; Zero out the 5-digit BCD number
xorw r_ax, r_ax
movb r_dl, r_al
mov cx, 16
.loop: ; 04d5
shl bx, 1
; Double al (bcd), and add in carry out from the shift
adcb r_al, r_al
daa
; Double ah (bcd) and propagate carry
xchg al, ah
adcb r_al, r_al
daa
xchg al, ah
; Double dl (bcd) and propagate carry
adcb r_dl, r_dl
loop .loop
print_bcd:
; Prints a 5-digit BCD number stored in DX:AX as such (x is ignored)
; dh dl ah al
; xx x1 23 45
mov bl, '0' - ' ' ; If value is zero, replace with space
xchg ax, dx
; First digit, low nybble of dl
call print_bcd_digit
; Next two digits, ah
movb r_al, r_dh
call print_bcd_byte
; Final two digits, al
movb r_al, r_dl
print_bcd_byte: ; 04f2
movb r_dh, r_al
times 4 shr al, 1
call print_bcd_digit
movb r_al, r_dh
print_bcd_digit: ; 0501
and al, 0xf
jz .to_ascii
mov bl, 0 ; Only apply replacement to zero values
.to_ascii:
add al, '0'
subb r_al, r_bl
jmp print_char
command_l: ; 050e
mov bx, [command_linenum]
orw r_bx, r_bx
jnz __0522
mov bx, [current_line]
sub bx, 11 ; magic
ja __0522
mov bx, 1
__0522:
call __0482
jnz __04b1 ; disambiguate
movw r_si, r_di
mov di, [__0a82]
inc di
subw r_di, r_bx
ja __053a
mov di, 0x17 ; magic
jmp __053a
__0537:
mov di, 1 ; magic
__053a:
mov cx, [last_file_byte]
subw r_cx, r_si
jz print_line.ret
mov bp, [current_line]
print_line: ; 0546
; Line number in bx, line data in si
push cx
call print_line_prefix
pop cx
.loop:
lodsb
; Print characters starting from space normally
cmp al, ' '
jnb .print_char
; Print CR, LF, TAB normally
cmp al, 10 ; LF
je .print_char
cmp al, 13 ; CR
je .print_char
cmp al, 9 ; TAB
je .print_char
; Print ^A for 0x01 and such
push ax
mov al, '^'
call print_char
pop ax
or al, 0x40 ; Transform control character to ^ equivalent
.print_char:
call print_char
cmp al, 10
loopne .loop
; TODO: Figure out what exactly is up with these
jcxz .ret
inc bx
dec di
jnz print_line
dec bx
.ret: ret
; in:
; si = line
; out:
; dx = length of line (untruncated)
; NOTE: Always ends a line with CR
__0574:
mov di, __0c1e.bufstart
mov cx, 255 ; Maximum line length
mov dx, -1 ; Start off at -1 since the loop increases dx before testing
.copyloop: ; 057d
lodsb
stosb
inc dx
cmp al, 13 ; CR
loopne .copyloop
mov [__0c1e.fill], dl
je print_line.ret
.find_cr: ; 058a
lodsb
inc dx
cmp al, 13 ; CR
jnz .find_cr
; Overwrite last character with CR
dec di
stosb
ret
__0593:
jmp print_not_found_str
command_r: ; 0596
call __0696
jnz __0593 ; disambiguate
__059b:
mov si, [__0a8c]
call __0574
sub dx, [__0a84]
mov cx, [__0a86]
addw r_dx, r_cx
cmp dx, 0xfe ; magic
ja __0622
mov bx, [__0a8a]
push dx
call print_line_prefix
pop dx
mov cx, [__0a88]
mov si, [__0a8c]
subw r_cx, r_si
dec cx
call __0618
push si
mov si, __0b9e
mov cx, [__0a86]
call __0618
pop si
add si, [__0a84]
movw r_cx, r_dx
add cx, 2 ; magic
call __0618
call prompt_okay
jne __0610 ; User didn't okay
call __0667
mov di, [__0a88]
dec di
mov si, __0b9e
mov dx, [__0a84]
mov cx, [__0a86]
dec cx
add [__0a88], cx
inc cx
dec dx
sub [__0a8e], dx
jnb __060c
mov word [__0a8e], 0
__060c:
inc dx
call __07d7
__0610:
call __06ef
jnz __0621 ; disambiguate
jmp near __059b
__0618:
jcxz __0621
__061a:
lodsb
call print_char
dec dx
loop __061a
__0621:
ret
__0622:
mov dx, line_too_long_error
jmp print_string
command_s: ; 0627
call __0696
jnz print_not_found_str ; disambiguate
__062c:
mov bx, [__0a8a]
mov si, [__0a8c]
call __0537
call prompt_okay
je __0667 ; User okayed
mov di, [__0a88]
mov cx, [__0a8e]
mov al, 10 ; magic
repne scasb
jne print_not_found_str
mov [__0a88], di
mov [__0a8c], di
mov [__0a8e], cx
inc word [__0a8a]
call __06ef
jz __062c ; disambiguate
print_not_found_str: ; 065f
mov dx, not_found_str
print_string: ; 0662
mov ah, sys_print_string
int 0x21
ret
__0667:
mov ax, [__0a8c]
mov [__0a92], ax
mov ax, [__0a8a]
mov [current_line], ax
.ret: ret
prompt_okay: ; 0674
test byte [__0a7d], 0xff
jz __0667.ret
mov dx, ok_prompt
mov ah, sys_print_string
int 0x21
mov ah, sys_read_char
int 0x21
push ax
call newline
pop ax
cmp al, 13 ; CR
je __0667.ret
cmp al, 'Y'
je __0667.ret
cmp al, 'y'
.ret: ret
__0696:
mov di, __0b1e
call copy_line
orb r_al, r_al ; TODO: Why does this exist?
jcxz prompt_okay.ret
mov [__0a84], cx
xorw r_cx, r_cx
cmp al, 13 ; CR
je __06b0
mov di, __0b9e
call copy_line
__06b0:
mov [__0a86], cx
mov bx, [command_linenum]
; If bx = 0, add 1 to bx
cmp bx, 1
adc bx, 0
call __0482
mov [__0a88], di
mov [__0a8c], di
mov [__0a8a], dx
mov bx, [__0a82]
; If bx ≠ 0, add 1 to bx
cmp bx, 1
sbb bx, -1
call __0482
movw r_cx, r_di
sub cx, [__0a88]
or al, 0xff ; TODO: Why does this exits?
jcxz prompt_okay.ret
sub cx, [__0a84]
jc prompt_okay.ret
inc cx
mov [__0a8e], cx
__06ef:
mov al, [__0b1e]
mov cx, [__0a8e]
mov di, [__0a88]
__06fa:
orw r_di, r_di
repne scasb
jne prompt_okay.ret
movw r_dx, r_cx
movw r_bx, r_di
mov cx, [__0a84]
dec cx
mov si, __0b1e + 1
cmpb r_al, r_al
repe cmpsb
movw r_cx, r_dx
movw r_di, r_bx
jne __06fa
mov [__0a8e], cx
movw r_cx, r_di
mov [__0a88], di
mov di, [__0a8c]
subw r_cx, r_di
mov al, 10 ; LF
mov dx, [__0a8a]
__072c:
inc dx
movw r_bx, r_di
repne scasb
je __072c
dec dx
mov [__0a8a], dx
mov [__0a8c], bx
xorb r_al, r_al
__073e:
ret
; in:
; si = source
; di = destination
; out:
; al = first non-copied character
; cx = amount copied
copy_line: ; 073f
xorw r_cx, r_cx
.loop:
lodsb
cmp al, 0x1a ; ^Z
jz __073e
cmp al, 13
jz __073e
stosb
inc cx
jmp .loop
command_d: ; 074e
mov bx, [command_linenum]
orw r_bx, r_bx
jnz __075a
mov bx, [current_line]
__075a:
call __0482
jnz __073e ; disambiguate
push bx
push di
mov bx, [__0a82]
orw r_bx, r_bx
jnz __076b
movw r_bx, r_dx
__076b:
inc bx
call __0482
movw r_dx, r_di
pop di
subw r_dx, r_di
jna __0782
pop word [current_line]
mov [__0a92], di
xorw r_cx, r_cx
jmp __07d7
__0782:
jmp print_entry_error
command_cr: ; 0785
mov bx, [command_linenum]
orw r_bx, r_bx
jnz __0792
mov bx, [current_line]
inc bx
__0792:
call __0482
movw r_si, r_di
mov [current_line], dx
mov [__0a92], si
jnz __073e ; disambiguate
cmp si, [last_file_byte]
je __073e
call __0574
mov [__0a84], dx
mov si, [__0a92]
call __0537
call print_line_prefix
mov ah, sys_read_line
mov dx, __0c1e
int 0x21
mov al, 10 ; LF
call print_char
mov cl, [__0c1e.fill]
mov ch, 0
jcxz __080b
mov dx, [__0a84]
mov si, __0c1e.bufstart
mov di, [__0a92]
__07d7:
cmpw r_cx, r_dx
je __0809
push si
push di
push cx
movw r_si, r_di
addw r_si, r_dx
addw r_di, r_cx
mov ax, [last_file_byte]
subw r_ax, r_dx
addw r_ax, r_cx
cmp ax, [last_valid_address]
jnb oom
xchg ax, [last_file_byte]
movw r_cx, r_ax
subw r_cx, r_si
; NOTE: Memmove?
cmpw r_si, r_di
ja __0802
addw r_si, r_cx
addw r_di, r_cx
std
__0802:
inc cx
rep movsb
cld
pop cx
pop di
pop si
__0809:
rep movsb
__080b:
ret
oom: ; 080c
mov dx, oom_str
mov ah, sys_print_string
int 0x21
jmp editor_mainloop
command_i: ; 0816
mov ax, sys_set_interrupt * 0x100 + int_ctrl_break
mov dx, __087d
int 0x21
mov bx, [command_linenum]
orw r_bx, r_bx
jnz __082a
mov bx, [current_line]
__082a:
call __0482
mov cx, [last_file_byte]
movw r_si, r_cx
subw r_cx, r_di
inc cx
mov di, [last_valid_address]
std
rep movsb
xchg di, si
cld
inc di
movw r_bp, r_si
movw r_bx, r_dx
__0845:
mov [__0a92], di
mov [current_line], bx
mov [last_file_byte], bp
call print_line_prefix
mov dx, __0c1e
mov ah, sys_read_line
int 0x21
call __0922
mov si, __0c1e.bufstart
cmpb_regind_ext b_si, 0x1a ; ^Z
je __088b
mov cl, [si - 1] ; magic
mov ch, 0
movw r_dx, r_si
addw r_dx, r_cx
inc dx
cmpw r_dx, r_bp
jnb oom
rep movsb
movsb
mov al, 10 ; magic, LF?
stosb
inc bx
jmp __0845
__087d:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, stack.end
call newline
__088b:
mov bp, [last_file_byte]
mov di, [__0a92]
movw r_si, r_bp
inc si
mov cx, [last_valid_address]
subw r_cx, r_bp
rep movsb
dec di
mov [last_file_byte], di
jmp editor_mainloop
command_q: ; 08a6
mov dx, quit_prompt
mov ah, sys_print_string
int 0x21
mov ah, sys_read_char
int 0x21
and al, 0x5f ; Lowercase
cmp al, 'Y'
jne newline
mov dx, new_file_fcb
mov ah, sys_close_file
int 0x21
mov ah, sys_delete_file
int 0x21
int 0x20
load_file_fully: ; 08c4
; Load 0xffff lines (= rest of file) off of disk
mov word [command_linenum], 0xffff
call command_a
command_e: ; 08cd
mov byte [__0a7f], 1 ; magic
mov bx, 0xffff ; magic
call __043b
test byte [new_file_flag], 0xff
jz load_file_fully
mov dx, [last_file_byte]
mov ah, sys_set_dta
int 0x21
mov cx, 1
mov dx, new_file_fcb
mov ah, sys_random_block_write
int 0x21
mov ah, sys_close_file
int 0x21
mov si, psp_fcb_1
; NOTE: - 1 since this copies the drive specifier (which is ignored?) too
lea di, [si + fcb_rename_target - 1]
movw r_dx, r_si
mov cx, 9
rep movsb
mov si, bak_extension
movsw
movsb
mov ah, sys_rename_file
int 0x21
mov si, psp_fcb_1
mov di, new_file_fcb + fcb_rename_target - 1
mov cx, 6 ; 6*2 = 12 bytes (as we copy words). 1 (drive specifier) + 8 (name) + 3 (extension)
rep movsw
mov dx, new_file_fcb
int 0x21
int 0x20
newline: ; 091d
mov al, 13 ; CR
call print_char
__0922:
mov al, 10 ; LF
print_char: ; 0924
push dx
xchg ax, dx
mov ah, sys_print_char
int 0x21
xchg ax, dx
pop dx
ret
ctrl_break_handler: ; 092d
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, stack.end
call newline
jmp editor_mainloop
bak_extension db "BAK" ; 093e
invalid_name_error db "Invalid drive or file name$" ; 0941
filename_missing_error db "File name must be specified$" ; 095c
bak_error db "Cannot edit .BAK file--rename file$" ; 0978
directory_full_error db "No room in directory for file$" ; 099b
disk_full_error db "Disk full--file write not completed$"
oom_str db 13, 10, "Insufficient memory", 13, 10, '$' ; 09dd
entry_error db "Entry error", 13, 10, '$' ; 09f5
new_file_str db "New file", 13, 10, '$' ; 0a03
not_found_str db "Not found", 13, 10, '$' ; 0a0e
ok_prompt db "O.K.? $" ; 0a1a
line_too_long_error db "Line too long", 13, 10, '$' ; 0a21
eof_str db "End of input file", 13, 10, '$' ; 0a31
quit_prompt db "Abort edit (Y/N)? $" ; 0a45
; 0a58
section .bss
new_file_fcb resb 37 ; 0a58 … 0a7c
__0a7d resb 1
new_file_flag resb 1 ; 0a7e
__0a7f resb 1
command_linenum resw 1 ; 0a80
__0a82 resw 1
__0a84 resw 1
__0a86 resw 1
__0a88 resw 1
__0a8a resw 1
__0a8c resw 1
__0a8e resw 1
current_line resw 1 ; 0a90
__0a92 resw 1
__0a94 resw 1
last_file_buffer_byte resw 1 ; 0a96
last_valid_address resw 1 ; 0a98
last_file_byte resw 1 ; 0a9a
input_buffer:
.size resb 1 ; 0a9c
.fill resb 1; 0a9d
.bufstart resb 128 ; 0a9e
.bufend: ; 0b1e
__0b1e resb 128
__0b9e resb 128
__0c1e:
.size resb 1 ; 0c1e
.fill resb 1 ; 0c1f
; TODO: Figure if this is 256 or 255 bytes
.bufstart resb 256 ; 0c20
.bufend: ; 0d20
stack: resb 40 ; 0d20
.end: ; 0d48
__0d48 resb 1
file_buffer: ; 0d49