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