1414 lines
22 KiB
NASM
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
|