-
Notifications
You must be signed in to change notification settings - Fork 0
/
B64_FC2.ASM
1087 lines (864 loc) · 22.4 KB
/
B64_FC2.ASM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
uppercase
bits 16
cpu 8086
; Include support for fortified DOS system calls:
%include "dossyscl.inc"
; Library routines used:
extern conv_bin_b64
extern conv_b64_bin
extern write_str_zterm
extern write_dossyscl_error
extern str_identity_check
extern char_is_space
segment dseg public class=data align=16
help_screen db "Usage: b64_fc2 [OPTIONS] <INPUT FILE> <OUTPUT FILE>", 0Dh, 0Ah
db 0Dh, 0Ah
db "b64_fc2 is a program used for encoding files into a Base64 representation, as", 0Dh, 0Ah
db "well as for decoding Base64-encoded files.", 0Dh, 0Ah
db 0Dh, 0Ah
db "Unlike the original, this version accepts the presence of blanks (eg. spaces,", 0Dh, 0Ah
db "newlines, tabs) within Base64-encoded input data, and it outputs at most 76", 0Dh, 0Ah
db "characters per line when encoding into Base64.", 0Dh, 0Ah
db 0Dh, 0Ah
db "Command-line options:", 0Dh, 0Ah
db 0Dh, 0Ah
db " -e, --encode Encode the given input file into Base64 (default)", 0Dh, 0Ah
db " -d, --decode Decode a Base64-encoded file", 0Dh, 0Ah
db " -s, --stdin Read input data from stdin", 0Dh, 0Ah
db " -c, --stdout Write output data to stdout", 0Dh, 0Ah
db " -h, --help Show this help screen and quit", 0Dh, 0Ah
db 0
welcome_str db "b64_fc2 - Dooshki's Base64 file encoding and decoding utility, v2."
double_nl_str db 0Dh, 0Ah
newline_str db 0Dh, 0Ah, 0
dot_newline_str db ".", 0Dh, 0Ah, 0
encode_lopt_str db "encode", 0
decode_lopt_str db "decode", 0
stdin_lopt_str db "stdin", 0
stdout_lopt_str db "stdout", 0
help_lopt_str db "help", 0
no_ifile_str db "No input file specified.", 0Dh, 0Ah, 0
no_ofile_str db "No output file specified.", 0Dh, 0Ah, 0
invalid_opt_str db "Invalid comman-line option: -", 0
unexp_arg_str db "Unexpected command-line argument: ", 0
usng_stdin_str db "Using stdin for data input.", 0Dh, 0Ah, 0
usng_stdout_str db "Using stdout for data output.", 0Dh, 0Ah, 0
opening_str db "Opening `", 0
opening_if_str db "' for reading... ", 0
opening_of_str db "' for writing... ", 0
encoding_str db 0Dh, 0Ah, "Encoding... ", 0
decoding_str db 0Dh, 0Ah, "Decoding... ", 0
success_str db "ok.", 0Dh, 0Ah, 0
done_str db "done.", 0Dh, 0Ah, 0
fail_str db "failed: ", 0
read_fail_str db "reading from the input file failed: ", 0
write_fail_str db "writing to the output file failed: ", 0
incompl_wr_str db "Incomplete write, probably due to the disk being full.", 0Dh, 0Ah, 0
decode_fail_str db "failed: The input data isn't valid Base64 data.", 0Dh, 0Ah, 0
bin_end_index resw 1
b64_end_index resw 1
; Help buffer used for processing of Base64 data to add or remove spaces.
;
hbuf_size equ 128 ; The size value must fit into
help_buffer resb hbuf_size ; an 8-bit variable, so 78-255.
bytes_in_hbuf db 0 ;
hbuf_cur_index db 0 ; The min size is 78 since the
; output routine saves 76 chars
; per line, with a CR LF pair.
in_file_handle resw 1
out_file_handle resw 1
close_in_file db 0
close_out_file db 0
operation db 1 ; 0 means do nothing, 1 means encode,
; 2 means decode.
segment cseg public class=code align=16
PSP: resw 1
..start: mov word [cs:PSP], ds ; Save pgm seg perfix.
mov ax, dseg
mov ds, ax
mov es, ax
mov ax, sseg
mov ss, ax
mov sp, stack_top
xor bp, bp
; The main routine shall set AL as the %ERRORLEVEL%
call near main
mov ah, 4Ch
int 21h
; Process command-line arguments and initialize program state:
;
;
; Inputs:
;
; Text on the command line.
;
; Outputs:
;
; This routine prints the welcome message onto the screen if the cmdline
; arguments were parsed successfully, and informs the user about the status
; of file utilization.
;
; It also prints error messages related to invalid usage.
;
; It also prints the help screen if neccessary, therefore the global
; show_help variable shall be used by the calling routine solely for
; determining whether to prematurely close the program and report success.
;
; NOTE: For the sake of simplicity, this routine doesn't preserve any GPRs.
;
program_init: push bp
mov bp, sp
; Given how in DOS, command-line argumetns are provided to the
; program in a string at most 127 bytes long, for the worst
; case scenario of a filename occupying the entire string, set
; aside 127 + 1 (zero termination) bytes for filenames.
;sub sp, 128 ; [bp-128] - First file name
;sub sp, 128 ; [bp-256] - Second file name
;sub sp, 1 ; [bp-257] - First file name preesnt?
;sub sp, 1 ; [bp-258] - Second file name preesnt?
;sub sp, 1 ; [bp-259] - stdin desired?
;sub sp, 1 ; [bp-260] - stdout desired?
;
; The max. size of a long command-line option, including the
; initial dash, shall be 9 + 1 (zero termination):
;sub sp, 10 ; [bp-270] - Long option buffer
;sub sp, 1 ; [bp-271] - Did a parsing error occur?
;sub sp, 1 ; [bp-272] - Unexpected argument?
; Allocate the above described stack space in a single swoop:
sub sp, 272
; Initialize stack-allocated variables:
mov byte [bp-257], 0
mov byte [bp-258], 0
mov byte [bp-259], 0
mov byte [bp-260], 0
mov byte [bp-271], 0
mov byte [bp-272], 0
; Save the old ES and access the Program Segment Prefix:
mov ax, es
push ax
mov ax, [cs:PSP]
mov es, ax
; Walk through the bytes of the command-line arguments buffer:
xor ch, ch
mov cl, [es:80h]
xor si, si
.main_loop: cmp si, cx
jnb .parsing_done
mov al, [es:81h+si]
inc si
call near char_is_space
jnc .main_loop
cmp al, '-'
jz .read_opts
jmp .read_fname
.read_opts: cmp si, cx
jnb .parsing_done
mov al, [es:81h+si]
inc si
call near char_is_space
jnc .main_loop
cmp al, '-'
jz .read_lopt
cmp al, 'e'
jz .opt_encode
cmp al, 'd'
jz .opt_decode
cmp al, 's'
jz .opt_stdin
cmp al, 'c'
jz .opt_stdout
cmp al, 'h'
jz .opt_help
jmp .opt_invalid
.opt_encode: mov byte [operation], 1
jmp .read_opts
.lopt_encode: mov byte [operation], 1
jmp .lopt_done
.opt_decode: mov byte [operation], 2
jmp .read_opts
.lopt_decode: mov byte [operation], 2
jmp .lopt_done
.opt_stdin: mov byte [bp-259], 1
jmp .read_opts
.lopt_stdin: mov byte [bp-259], 1
jmp .lopt_done
.opt_stdout: mov byte [bp-260], 1
jmp .read_opts
.lopt_stdout: mov byte [bp-260], 1
jmp .lopt_done
.opt_help: mov byte [operation], 0
jmp .read_opts
.lopt_help: mov byte [operation], 0
jmp .lopt_done
.lopt_done: pop ax
mov es, ax
jmp .main_loop
.opt_invalid: mov byte [bp-271], 1
; Since no files have been opened yet, we can use a file
; handle variables to temporarily store things in the data
; segment.
mov [in_file_handle], al
mov bx, stderr
mov dx, invalid_opt_str
call near write_str_zterm
push cx
mov cx, 1
mov dx, in_file_handle
call near dos_syscl_hwrite
mov cx, 2
mov dx, newline_str
call near dos_syscl_hwrite
pop cx
jmp .read_opts
.read_lopt: xor di, di
mov byte [bp-270+di], al
inc di
.rlopt_loop: cmp si, cx
jnb .lopt_loaded
mov al, [es:81h+si]
inc si
call near char_is_space
jnc .lopt_loaded
cmp di, 9 ; The lopt buffer is 9 + 1 (zterm) long.
jnb .lopt_too_long
mov [bp-270+di], al
inc di
jmp .rlopt_loop
.lopt_loaded: mov byte [bp-270+di], 0
lea bx, [bp-270+1] ; +1 for the initial dash.
mov ax, es
push ax
mov ax, ss
mov es, ax ; es:bx is now the loaded lopt.
mov dx, encode_lopt_str
call near str_identity_check
jnc .lopt_encode
mov dx, decode_lopt_str
call near str_identity_check
jnc .lopt_decode
mov dx, stdin_lopt_str
call near str_identity_check
jnc .lopt_stdin
mov dx, stdout_lopt_str
call near str_identity_check
jnc .lopt_stdout
mov dx, help_lopt_str
call near str_identity_check
jnc .lopt_help
pop ax
mov es, ax
jmp .lopt_invalid
.lopt_too_long: mov byte [bp-270+di], 0
.deplete_loop: cmp si, cx
jnb .deplete_complete
mov al, [es:81h+si]
inc si
call near char_is_space
jnc .deplete_complete
jmp .deplete_loop
.deplete_complete:
.lopt_invalid: mov bx, stderr
mov dx, invalid_opt_str
call near write_str_zterm
mov ax, ds
push ax
mov ax, ss
mov ds, ax
lea dx, [bp-270]
call near write_str_zterm
pop ax
mov ds, ax
mov dx, newline_str
call near write_str_zterm
mov byte [bp-271], 1
jmp .main_loop
.read_fname: cmp byte [bp-257], 0 ; Input file known?
jnz .have_file1
cmp byte [bp-259], 0 ; stdin desired?
jnz .have_file1
mov byte [bp-257], 1
lea bx, [bp-128]
jmp .load_fname
.have_file1: cmp byte [bp-258], 0 ; Output file known?
jnz .have_file2
cmp byte [bp-260], 0 ; stdout desired?
jnz .have_file2
mov byte [bp-258], 1
lea bx, [bp-256]
jmp .load_fname
; Unexpected argument? Well, since we'll error out, we won't
; end up opening any files, so we might as well load this
; unexpected argument into the output file name buffer, so that
; we can print it in an error message.
.have_file2: mov byte [bp-272], 1
lea bx, [bp-256]
.load_fname: mov [ss:bx], al
inc bx
.fnload_loop: cmp si, cx
jnb .fname_loaded
mov al, [es:81h+si]
inc si
call near char_is_space
jnc .fname_loaded
mov [ss:bx], al
inc bx
jmp .fnload_loop
.fname_loaded: mov byte [ss:bx], 0
; Sure, we've loaded something, but do we want it?
cmp byte [bp-272], 0
jz .main_loop
mov bx, stderr
mov dx, unexp_arg_str
call near write_str_zterm
mov ax, ds
push ax
mov ax, ss
mov ds, ax
lea dx, [bp-256]
call near write_str_zterm
pop ax
mov ds, ax
mov dx, newline_str
call near write_str_zterm
mov byte [bp-271], 1
jmp .main_loop
.parsing_done: cmp byte [bp-271], 0
jz .parsing_ok
.err_showhelp: mov byte [operation], 0
mov bx, stderr
mov dx, help_screen
call near write_str_zterm
; The input file might be open if we failed to open the
; output file.
.err_nohelp: call near close_files
.err_noiclose: stc
jmp .epilogue
.parsing_ok: cmp byte [operation], 0
jz .show_help
; Sanity check:
cmp byte [bp-257], 0 ; Input file known?
jnz .have_ifile
cmp byte [bp-259], 0 ; stdin desired?
jnz .have_ifile
mov bx, stderr
mov dx, no_ifile_str
call near write_str_zterm
mov byte [bp-271], 1
.have_ifile: cmp byte [bp-258], 0 ; Output file known?
jnz .have_ofile
cmp byte [bp-260], 0 ; stdout desired?
jnz .have_ofile
mov bx, stderr
mov dx, no_ofile_str
call near write_str_zterm
mov byte [bp-271], 1
.have_ofile: cmp byte [bp-271], 0
jnz .err_showhelp
; Now that we've reached this point, we can show the program's
; welcome message:
mov bx, stderr
mov dx, welcome_str
call near write_str_zterm
; Since we'll be switching between segments, have them ready.
mov si, ss
mov di, ds
.ready_ifile: cmp byte [bp-259], 0 ; stdin desired?
jz .open_ifile
mov dx, usng_stdin_str
call near write_str_zterm
mov word [in_file_handle], stdin
mov byte [close_in_file], 0
jmp .ready_ofile
.open_ifile: mov dx, opening_str
call near write_str_zterm
mov ds, si
lea dx, [bp-128]
call near write_str_zterm
mov ds, di
mov dx, opening_if_str
call near write_str_zterm
mov ds, si
lea dx, [bp-128]
mov al, file_read_only
call near dos_syscl_hfopen
mov ds, di
jnc .ifopen_ok
mov cx, ax
mov dx, fail_str
call near write_str_zterm
mov ax, cx
call near write_dossyscl_error
jmp .err_nohelp
.ifopen_ok: mov [in_file_handle], ax
mov byte [close_in_file], 1
mov dx, success_str
call near write_str_zterm
.ready_ofile: cmp byte [bp-260], 0 ; stdout desired?
jz .open_ofile
mov dx, usng_stdout_str
call near write_str_zterm
mov word [out_file_handle], stdout
mov byte [close_out_file], 0
jmp .all_done
.open_ofile: mov dx, opening_str
call near write_str_zterm
mov ds, si
lea dx, [bp-256]
call near write_str_zterm
mov ds, di
mov dx, opening_of_str
call near write_str_zterm
mov ds, si
lea dx, [bp-256]
xor cx, cx
call near dos_syscl_hfcreate
mov ds, di
jnc .ofopen_ok
mov cx, ax
mov dx, fail_str
call near write_str_zterm
mov ax, cx
call near write_dossyscl_error
jmp .err_nohelp
.ofopen_ok: mov [out_file_handle], ax
mov byte [close_out_file], 1
mov dx, success_str
call near write_str_zterm
.all_done: clc
jmp .epilogue
.show_help: mov bx, stdout
mov dx, help_screen
call near write_str_zterm
clc
.epilogue: pop ax
mov es, ax
mov sp, bp
pop bp
retn
; Close open files:
;
; Inputs:
;
; Program state within the data segment.
;
; Outputs:
;
; None.
;
; The values of all GPRs are preserved.
;
close_files: push ax
push bx
cmp byte [close_in_file], 0
jz .no_close_in
mov bx, [in_file_handle]
call near dos_syscl_hclose
mov byte [close_in_file], 0
mov word [in_file_handle], 0
.no_close_in:
cmp byte [close_out_file], 0
jz .no_close_out
mov bx, [out_file_handle]
call near dos_syscl_hclose
mov byte [close_out_file], 0
mov word [out_file_handle], 0
.no_close_out:
pop bx
pop ax
retn
; The main routine of the program:
;
main: call near program_init
jnc .init_ok
; If we failed to initialize, return a non-zero exit status.
mov al, 1
retn
.init_ok: cmp byte [operation], 0
jnz .check_op
; Only printed a help screen? In that case, we're done here.
xor al, al
retn
; Determine which operation to perform:
.check_op: cmp byte [operation], 1
jnz .perform_dec
; Encode the input file:
.perform_enc: mov bx, stderr
mov dx, encoding_str
call near write_str_zterm
mov si, bin_buf_seg
mov di, b64_buf_seg
mov ax, ds
push ax
mov es, di
.enc_loop: mov bx, [in_file_handle]
mov cx, 48 * 1024
mov ds, si
xor dx, dx
call near dos_syscl_hread
jc .read_failed
test ax, ax
jnz .enc_not_done
pop ax
mov ds, ax
push ax
mov cx, [out_file_handle]
call near flush_nl_save_buf
jnc .done
test ax, ax
jz .write_short
jmp .write_failed
.enc_not_done: mov cx, ax
dec cx
mov bx, si
mov dx, di
pop ax
mov ds, ax
push ax
call near conv_bin_b64
mov dx, cx
xor bx, bx
mov cx, [out_file_handle]
call near save_with_nl
jnc .enc_loop
test ax, ax
jz .write_short
jmp .write_failed
; Decode the input file:
.perform_dec: mov bx, stderr
mov dx, decoding_str
call near write_str_zterm
mov si, b64_buf_seg
mov di, bin_buf_seg
mov ax, ds
push ax
mov es, si
.dec_loop: xor bx, bx
mov dx, 0FFFFh
mov cx, [in_file_handle]
call near load_no_spaces
jnc .buf_read_ok
test ax, ax
jz .done
jmp .read_failed
.buf_read_ok: mov cx, dx
mov bx, si
mov dx, di
call near conv_b64_bin
jc .dec_failed
mov bx, [out_file_handle]
mov ds, di
xor dx, dx
inc cx
call near dos_syscl_hwrite
jc .write_failed
cmp ax, cx
jnz .write_short
pop ax
mov ds, ax
push ax
jmp .dec_loop
.read_failed: mov di, ax
pop ax
mov ds, ax
mov bx, stderr
mov dx, read_fail_str
call near write_str_zterm
mov ax, di
call near write_dossyscl_error
call near close_files
mov al, 2
retn
.dec_failed: pop ax
mov ds, ax
mov bx, stderr
mov dx, decode_fail_str
call near write_str_zterm
call near close_files
mov al, 3
retn
.write_failed: mov di, ax
pop ax
mov ds, ax
mov bx, stderr
mov dx, write_fail_str
call near write_str_zterm
mov ax, di
call near write_dossyscl_error
call near close_files
mov al, 2
retn
.write_short: pop ax
mov ds, ax
mov bx, stderr
mov dx, write_fail_str
call near write_str_zterm
mov dx, incompl_wr_str
call near write_str_zterm
call near close_files
mov al, 2
retn
.done: pop ax
mov ds, ax
mov bx, stderr
mov dx, done_str
call near write_str_zterm
call near close_files
xor al, al
retn
; Load data from a file, omitting spaces:
;
;
; This routine loads data from a specified file and removes any whitespace
; characters from it. It's useful for eg. stripping separator characters from
; Base64-encoded data.
;
; ES:DX will, upon return, point to the last available character that was
; successfully read. If no character was read, but not because of an error,
; the carry flag is set and AX contains zero.
;
;
; Inputs:
;
; DS has to be set to the data segment of this module.
;
; CX - File handle.
; ES:BX - Start index of the buffer to populate.
; ES:DX - Index of the last usable byte within the buffer.
;
;
; Outputs:
;
; The carry flag is set if no data was read, clear on a successful read.
;
; AX - Restored on success,
; Error code from dos_syscl_hread (or 0 on an empty read) on failure.
;
; ES:DX - Index of the last byte loaded into the buffer on success,
; restored on failure.
;
; The values of all other GPRs are preserved.
;
load_no_spaces: push bp
mov bp, sp
push ax ; [bp-2]
push bx ; [bp-4] - current output buffer index.
push dx ; [bp-6]
push bx ; [bp-8]
push cx ; [bp-10]
push si ; [bp-12]
push di ; [bp-14]
mov al, [bytes_in_hbuf]
xor ah, ah
test ax, ax
jz .load_hbuf
mov di, ax
mov al, [hbuf_cur_index]
xor ah, ah
mov si, ax
jmp .load_obuf
.load_hbuf: mov bx, [bp-10]
mov cx, hbuf_size
mov dx, help_buffer
call near dos_syscl_hread
jnc .read_okay
mov dx, [bp-6]
jmp .epilogue
.read_okay: test ax, ax
jnz .got_bytes
mov dx, [bp-4]
cmp dx, [bp-8]
jz .empty_read
dec dx
mov ax, [bp-2]
clc
jmp .epilogue
; No bytes at all were placed into the output buffer by us:
.empty_read: mov dx, [bp-6]
stc
jmp .epilogue
.got_bytes: mov [bytes_in_hbuf], al
mov di, ax
xor si, si
.load_obuf: mov bx, [bp-4]
mov dx, [bp-6]
.lob_loop: mov al, [help_buffer+si]
call near char_is_space
jnc .next_hbbyte
mov [es:bx], al
cmp bx, dx
jz .obuf_filled
inc bx
jmp .next_hbbyte
.obuf_filled: inc si
cmp si, di
jz .hbuf_empty
mov ax, si
mov [hbuf_cur_index], al
jmp .load_done
.hbuf_empty: mov byte [bytes_in_hbuf], 0
jmp .load_done
.next_hbbyte: inc si
cmp si, di
jb .lob_loop
mov byte [bytes_in_hbuf], 0
mov [bp-4], bx
jmp .load_hbuf
; If we exit via this path, the entire output buffer was filled:
.load_done: mov ax, [bp-2]
mov dx, [bp-6]
clc
.epilogue: pop di
pop si
pop cx
pop bx
mov sp, bp
pop bp
retn
; Save the provided data into a file, at most 76 characters a line:
;
;
; This routine takes the data from the provided buffer and saves it, line by
; line, into an file, using the handle interface.
;
; The routine has a persistent buffer that needs to be flushed upon loading all
; of the data you wish to save through this routine, use the flush_nl_save_buf
; routine for this.
;
;
; Inputs:
;
; DS has to be set to the data segment of this module.
;
; CX - File handle.
; ES:BX - Start index of the buffer to read from.
; ES:DX - Index of the last byte within the buffer.
;
;
; Outputs:
;
; The carry flag is set if no data was read, clear on a successful read.
;
; AX - Restored on success,
; Error code from dos_syscl_hwrite (or 0 if short write) on failure.
;
; The values of all other GPRs are preserved.
;
save_with_nl: push bp
mov bp, sp
push ax ; [bp-2]
push bx ; [bp-4] - current input buffer index.
push bx ; [bp-6]
push cx ; [bp-8]
push dx ; [bp-10]
push si ; [bp-12]
push di ; [bp-14]
mov al, [bytes_in_hbuf]
xor ah, ah
mov si, ax
mov di, 76
.ldlnbuf_loop: mov al, [es:bx]
mov [help_buffer+si], al
inc si
cmp si, di
jz .ldlnbuf_full
.cont_loading: cmp bx, dx
jz .input_loaded
inc bx
jmp .ldlnbuf_loop
.ldlnbuf_full: mov [bp-4], bx
mov byte [help_buffer+si], 0Dh
inc si
mov byte [help_buffer+si], 0Ah
inc si
mov bx, [bp-8]
mov cx, si
mov dx, help_buffer
call near dos_syscl_hwrite
jc .epilogue
cmp ax, cx
jz .write_ok
xor ax, ax
stc
jmp .epilogue
.write_ok: mov bx, [bp-4]
mov dx, [bp-10]
xor si, si
jmp .cont_loading
.input_loaded: mov ax, si
mov [bytes_in_hbuf], al
mov ax, [bp-2]
clc
.epilogue: pop di
pop si
pop dx