summaryrefslogtreecommitdiff
path: root/v2.0/source/DEBCOM1.ASM
blob: 8e992d97a176c9361beb97efeeba2536444d256a (plain) (blame)
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
TITLE   PART1 DEBUGGER COMMANDS

; Routines to perform debugger commands except ASSEMble and UASSEMble

.xlist
.xcref
        INCLUDE DEBEQU.ASM
        INCLUDE DOSSYM.ASM
.cref
.list

CODE    SEGMENT PUBLIC BYTE 'CODE'
CODE    ENDS

CONST   SEGMENT PUBLIC BYTE

        EXTRN   SYNERR:BYTE

        EXTRN   DISPB:WORD,DSIZ:BYTE,DSSAVE:WORD
        if  sysver
        EXTRN   CIN:DWORD,PFLAG:BYTE
        endif

CONST   ENDS

DATA    SEGMENT PUBLIC BYTE

        EXTRN   DEFLEN:WORD,BYTEBUF:BYTE,DEFDUMP:BYTE

DATA    ENDS

DG      GROUP   CODE,CONST,DATA


CODE    SEGMENT PUBLIC BYTE 'CODE'
ASSUME  CS:DG,DS:DG,ES:DG,SS:DG


        PUBLIC  HEXCHK,GETHEX1,PRINT,DSRANGE,ADDRESS,HEXIN,PERROR
        PUBLIC  GETHEX,GET_ADDRESS,GETEOL,GETHX,PERR
        PUBLIC  PERR,MOVE,DUMP,ENTER,FILL,SEARCH,DEFAULT
        if  sysver
        PUBLIC  IN
        EXTRN   DISPREG:NEAR,DEVIOCALL:NEAR
        endif

        EXTRN   OUT:NEAR,CRLF:NEAR,OUTDI:NEAR,OUTSI:NEAR,SCANP:NEAR
        EXTRN   SCANB:NEAR,BLANK:NEAR,TAB:NEAR,PRINTMES:NEAR,COMMAND:NEAR
        EXTRN   HEX:NEAR,BACKUP:NEAR


DEBCOM1:

; RANGE - Looks for parameters defining an address range.
; The first parameter is the starting address. The second parameter
; may specify the ending address, or it may be preceded by
; "L" and specify a length (4 digits max), or it may be
; omitted and a length of 128 bytes is assumed. Returns with
; segment in AX, displacement in DX, and length in CX.

DSRANGE:
        MOV     BP,[DSSAVE]             ; Set default segment to DS
        MOV     [DEFLEN],128            ; And default length to 128 bytes
RANGE:
        CALL    ADDRESS
        PUSH    AX                      ; Save segment
        PUSH    DX                      ; Save offset
        CALL    SCANP                   ; Get to next parameter
        MOV     AL,[SI]
        CMP     AL,"L"                  ; Length indicator?
        JE      GETLEN
        MOV     DX,[DEFLEN]             ; Default length
        CALL    HEXIN                   ; Second parameter present?
        JC      GetDef                  ; If not, use default
        MOV     CX,4
        CALL    GETHEX                  ; Get ending address (same segment)
        MOV     CX,DX                   ; Low 16 bits of ending addr.
        POP     DX                      ; Low 16 bits of starting addr.
        SUB     CX,DX                   ; Compute range
        JAE     DSRNG2
DSRNG1: JMP     PERROR                  ; Negative range
DSRNG2: INC     CX                      ; Include last location
        JCXZ    DSRNG1                  ; Wrap around error
        POP     AX                      ; Restore segment
        RET
GetDef:
        POP     CX                      ; get original offset
        PUSH    CX                      ; save it
        NEG     CX                      ; rest of segment
        JZ      RngRet                  ; use default
        CMP     CX,DX                   ; more room in segment?
        JAE     RngRet                  ; yes, use default
        JMP     RngRet1                 ; no, length is in CX

GETLEN:
        INC     SI                      ; Skip over "L" to length
        MOV     CX,4                    ; Length may have 4 digits
        CALL    GETHEX                  ; Get the range
RNGRET:
        MOV     CX,DX                   ; Length
RngRet1:
        POP     DX                      ; Offset
        MOV     AX,CX
        ADD     AX,DX
        JNC     OKRET
        CMP     AX,1
        JAE     DSRNG1                  ; Look for wrap error
OKRET:
        POP     AX                      ; Segment
        RET

DEFAULT:
; DI points to default address and CX has default length
        CALL    SCANP
        JZ      USEDEF                  ; Use default if no parameters
        MOV     [DEFLEN],CX
        CALL    RANGE
        JMP     GETEOL
USEDEF:
        MOV     SI,DI
        LODSW                           ; Get default displacement
        MOV     DX,AX
        LODSW                           ; Get default segment
        RET

; Dump an area of memory in both hex and ASCII

DUMP:
        MOV     BP,[DSSAVE]
        MOV     CX,DISPB
        MOV     DI,OFFSET DG:DEFDUMP
        CALL    DEFAULT                 ; Get range if specified
        MOV     DS,AX                   ; Set segment
        MOV     SI,DX                   ; SI has displacement in segment

        IF  ZIBO
        PUSH    SI                      ; save SI away
        AND     SI,0FFF0h               ; convert to para number
        CALL    OutSI                   ; display location
        POP     SI                      ; get SI back
        MOV     AX,SI                   ; move offset
        MOV     AH,3                    ; spaces per byte
        AND     AL,0Fh                  ; convert to real offset
        MUL     AH                      ; compute (AL+1)*3-1
        OR      AL,AL                   ; set flag
        JZ      InRow                   ; if xero go on
        PUSH    CX                      ; save count
        MOV     CX,AX                   ; move to convenient spot
        CALL    Tab                     ; move over
        POP     CX                      ; get back count
        JMP     InRow                   ; display line
        ENDIF

ROW:
        CALL    OUTSI                   ; Print address at start of line
InRow:
        PUSH    SI                      ; Save address for ASCII dump
        CALL    BLANK
BYTE0:
        CALL    BLANK                   ; Space between bytes
BYTE1:
        LODSB                           ; Get byte to dump
        CALL    HEX                     ; and display it
        POP     DX                      ; DX has start addr. for ASCII dump
        DEC     CX                      ; Drop loop count
        JZ      ToAscii                 ; If through do ASCII dump
        MOV     AX,SI
        TEST    AL,CS:(BYTE PTR DSIZ)   ; On 16-byte boundary?
        JZ      ENDROW
        PUSH    DX                      ; Didn't need ASCII addr. yet
        TEST    AL,7                    ; On 8-byte boundary?
        JNZ     BYTE0
        MOV     AL,"-"                  ; Mark every 8 bytes
        CALL    OUT
        JMP     SHORT BYTE1
ENDROW:
        CALL    ASCII                   ; Show it in ASCII
        JMP     SHORT ROW               ; Loop until count is zero
ToAscii:
        MOV     AX,SI                   ; get offset
        AND     AL,0Fh                  ; real offset
        JZ      ASCII                   ; no loop if already there
        SUB     AL,10h                  ; remainder
        NEG     AL
        MOV     CL,3
        MUL     CL
        MOV     CX,AX                   ; number of chars to move
        CALL    Tab
ASCII:
        PUSH    CX                      ; Save byte count
        MOV     AX,SI                   ; Current dump address
        MOV     SI,DX                   ; ASCII dump address
        SUB     AX,DX                   ; AX=length of ASCII dump
        IF NOT ZIBO
; Compute tab length. ASCII dump always appears on right side
; screen regardless of how many bytes were dumped. Figure 3
; characters for each byte dumped and subtract from 51, which
; allows a minimum of 3 blanks after the last byte dumped.
        MOV     BX,AX
        SHL     AX,1                    ; Length times 2
        ADD     AX,BX                   ; Length times 3
        MOV     CX,51
        SUB     CX,AX                   ; Amount to tab in CX
        CALL    TAB
        MOV     CX,BX                   ; ASCII dump length back in CX
        ELSE
        MOV     CX,SI                   ; get starting point
        DEC     CX
        AND     CX,0Fh
        INC     CX
        AND     CX,0Fh
        ADD     CX,3                    ; we have the correct number to tab
        PUSH    AX                      ; save count
        CALL    TAB
        POP     CX                      ; get count back
        ENDIF
ASCDMP:
        LODSB                           ; Get ASCII byte to dump
        AND     AL,7FH                  ; ASCII uses 7 bits
        CMP     AL,7FH                  ; Don't try to print RUBOUT
        JZ      NOPRT
        CMP     AL," "                  ; Check for control characters
        JNC     PRIN
NOPRT:
        MOV     AL,"."                  ; If unprintable character
PRIN:
        CALL    OUT                     ; Print ASCII character
        LOOP    ASCDMP                  ; CX times
        POP     CX                          ; Restore overall dump length
        MOV     ES:WORD PTR [DEFDUMP],SI
        MOV     ES:WORD PTR [DEFDUMP+2],DS  ; Save last address as default
        CALL    CRLF                        ; Print CR/LF and return
        RET


; Block move one area of memory to another. Overlapping moves
; are performed correctly, i.e., so that a source byte is not
; overwritten until after it has been moved.

MOVE:
        CALL    DSRANGE                 ; Get range of source area
        PUSH    CX                      ; Save length
        PUSH    AX                      ; Save segment
        PUSH    DX                      ; Save source displacement
        CALL    ADDRESS                 ; Get destination address (same segment)
        CALL    GETEOL                  ; Check for errors
        POP     SI
        MOV     DI,DX                   ; Set dest. displacement
        POP     BX                      ; Source segment
        MOV     DS,BX
        MOV     ES,AX                   ; Destination segment
        POP     CX                      ; Length
        CMP     DI,SI                   ; Check direction of move
        SBB     AX,BX                   ; Extend the CMP to 32 bits
        JB      COPYLIST                ; Move forward into lower mem.
; Otherwise, move backward. Figure end of source and destination
; areas and flip direction flag.
        DEC     CX
        ADD     SI,CX                   ; End of source area
        ADD     DI,CX                   ; End of destination area
        STD                             ; Reverse direction
        INC     CX
COPYLIST:
        MOVSB                           ; Do at least 1 - Range is 1-10000H not 0-FFFFH
        DEC     CX
        REP     MOVSB                   ; Block move
RET1:   RET

; Fill an area of memory with a list values. If the list
; is bigger than the area, don't use the whole list. If the
; list is smaller, repeat it as many times as necessary.

FILL:
        CALL    DSRANGE                 ; Get range to fill
        PUSH    CX                      ; Save length
        PUSH    AX                      ; Save segment number
        PUSH    DX                      ; Save displacement
        CALL    LIST                    ; Get list of values to fill with
        POP     DI                      ; Displacement in segment
        POP     ES                      ; Segment
        POP     CX                      ; Length
        CMP     BX,CX                   ; BX is length of fill list
        MOV     SI,OFFSET DG:BYTEBUF    ; List is in byte buffer
        JCXZ    BIGRNG
        JAE     COPYLIST                ; If list is big, copy part of it
BIGRNG:
        SUB     CX,BX                   ; How much bigger is area than list?
        XCHG    CX,BX                   ; CX=length of list
        PUSH    DI                      ; Save starting addr. of area
        REP     MOVSB                   ; Move list into area
        POP     SI
; The list has been copied into the beginning of the
; specified area of memory. SI is the first address
; of that area, DI is the end of the copy of the list
; plus one, which is where the list will begin to repeat.
; All we need to do now is copy [SI] to [DI] until the
; end of the memory area is reached. This will cause the
; list to repeat as many times as necessary.
        MOV     CX,BX                   ; Length of area minus list
        PUSH    ES                      ; Different index register
        POP     DS                      ; requires different segment reg.
        JMP     SHORT COPYLIST          ; Do the block move

; Search a specified area of memory for given list of bytes.
; Print address of first byte of each match.

SEARCH:
        CALL    DSRANGE                 ; Get area to be searched
        PUSH    CX                      ; Save count
        PUSH    AX                      ; Save segment number
        PUSH    DX                      ; Save displacement
        CALL    LIST                    ; Get search list
        DEC     BX                      ; No. of bytes in list-1
        POP     DI                      ; Displacement within segment
        POP     ES                      ; Segment
        POP     CX                      ; Length to be searched
        SUB     CX,BX                   ;  minus length of list
SCAN:
        MOV     SI,OFFSET DG:BYTEBUF    ; List kept in byte buffer
        LODSB                           ; Bring first byte into AL
DOSCAN:
        SCASB                           ; Search for first byte
        LOOPNE  DOSCAN                  ; Do at least once by using LOOP
        JNZ     RET1                    ; Exit if not found
        PUSH    BX                      ; Length of list minus 1
        XCHG    BX,CX
        PUSH    DI                      ; Will resume search here
        REPE    CMPSB                   ; Compare rest of string
        MOV     CX,BX                   ; Area length back in CX
        POP     DI                      ; Next search location
        POP     BX                      ; Restore list length
        JNZ     TEST                    ; Continue search if no match
        DEC     DI                      ; Match address
        CALL    OUTDI                   ; Print it
        INC     DI                      ; Restore search address
        CALL    CRLF
TEST:
        JCXZ    RET1
        JMP     SHORT SCAN              ; Look for next occurrence

; Get the next parameter, which must be a hex number.
; CX is maximum number of digits the number may have.

GETHX:
        CALL    SCANP
GETHX1:
        XOR     DX,DX                   ; Initialize the number
        CALL    HEXIN                   ; Get a hex digit
        JC      HXERR                   ; Must be one valid digit
        MOV     DL,AL                   ; First 4 bits in position
GETLP:
        INC     SI                      ; Next char in buffer
        DEC     CX                      ; Digit count
        CALL    HEXIN                   ; Get another hex digit?
        JC      RETHX                   ; All done if no more digits
        STC
        JCXZ    HXERR                   ; Too many digits?
        SHL     DX,1                    ; Multiply by 16
        SHL     DX,1
        SHL     DX,1
        SHL     DX,1
        OR      DL,AL                   ; and combine new digit
        JMP     SHORT GETLP             ; Get more digits

GETHEX:
        CALL    GETHX                   ; Scan to next parameter
        JMP     SHORT GETHX2
GETHEX1:
        CALL    GETHX1
GETHX2: JC      PERROR
RETHX:  CLC
HXERR:  RET


; Check if next character in the input buffer is a hex digit
; and convert it to binary if it is. Carry set if not.

HEXIN:
        MOV     AL,[SI]

; Check if AL has a hex digit and convert it to binary if it
; is. Carry set if not.

HEXCHK:
        SUB     AL,"0"                  ; Kill ASCII numeric bias
        JC      RET2
        CMP     AL,10
        CMC
        JNC     RET2                    ; OK if 0-9
        AND     AL,5FH
        SUB     AL,7                    ; Kill A-F bias
        CMP     AL,10
        JC      RET2
        CMP     AL,16
        CMC
RET2:   RET

; Process one parameter when a list of bytes is
; required. Carry set if parameter bad. Called by LIST.

LISTITEM:
        CALL    SCANP                   ; Scan to parameter
        CALL    HEXIN                   ; Is it in hex?
        JC      STRINGCHK               ; If not, could be a string
        MOV     CX,2                    ; Only 2 hex digits for bytes
        CALL    GETHEX                  ; Get the byte value
        MOV     [BX],DL                 ; Add to list
        INC     BX
GRET:   CLC                             ; Parameter was OK
        RET
STRINGCHK:
        MOV     AL,[SI]                 ; Get first character of param
        CMP     AL,"'"                  ; String?
        JZ      STRING
        CMP     AL,'"'                  ; Either quote is all right
        JZ      STRING
        STC                             ; Not string, not hex - bad
        RET
STRING:
        MOV     AH,AL                   ; Save for closing quote
        INC     SI
STRNGLP:
        LODSB                           ; Next char of string
        CMP     AL,13                   ; Check for end of line
        JZ      PERR                    ; Must find a close quote
        CMP     AL,AH                   ; Check for close quote
        JNZ     STOSTRG                 ; Add new character to list
        CMP     AH,[SI]                 ; Two quotes in a row?
        JNZ     GRET                    ; If not, we're done
        INC     SI                      ; Yes - skip second one
STOSTRG:
        MOV     [BX],AL                 ; Put new char in list
        INC     BX
        JMP     SHORT STRNGLP           ; Get more characters

; Get a byte list for ENTER, FILL or SEARCH. Accepts any number
; of 2-digit hex values or character strings in either single
; (') or double (") quotes.

LIST:
        MOV     BX,OFFSET DG:BYTEBUF    ; Put byte list in the byte buffer
LISTLP:
        CALL    LISTITEM                ; Process a parameter
        JNC     LISTLP                  ; If OK, try for more
        SUB     BX,OFFSET DG:BYTEBUF    ; BX now has no. of bytes in list
        JZ      PERROR                  ; List must not be empty

; Make sure there is nothing more on the line except for
; blanks and carriage return. If there is, it is an
; unrecognized parameter and an error.

GETEOL:
        CALL    SCANB                   ; Skip blanks
        JNZ     PERROR                  ; Better be a RETURN
RET3:   RET

; Command error. SI has been incremented beyond the
; command letter so it must decremented for the
; error pointer to work.

PERR:
        DEC     SI

; Syntax error. SI points to character in the input buffer
; which caused error. By subtracting from start of buffer,
; we will know how far to tab over to appear directly below
; it on the terminal. Then print "^ Error".

PERROR:
        SUB     SI,OFFSET DG:(BYTEBUF-1); How many char processed so far?
        MOV     CX,SI                   ; Parameter for TAB in CX
        CALL    TAB                     ; Directly below bad char
        MOV     SI,OFFSET DG:SYNERR     ; Error message

; Print error message and abort to command level

PRINT:
        CALL    PRINTMES
        JMP     COMMAND

; Gets an address in Segment:Displacement format. Segment may be omitted
; and a default (kept in BP) will be used, or it may be a segment
; register (DS, ES, SS, CS). Returns with segment in AX, OFFSET in DX.

ADDRESS:
        CALL    GET_ADDRESS
        JC      PERROR
ADRERR: STC
        RET

GET_ADDRESS:
        CALL    SCANP
        MOV     AL,[SI+1]
        CMP     AL,"S"
        JZ      SEGREG
        MOV     CX,4
        CALL    GETHX
        JC      ADRERR
        MOV     AX,BP                   ; Get default segment
        CMP     BYTE PTR [SI],":"
        JNZ     GETRET
        PUSH    DX
GETDISP:
        INC     SI                      ; Skip over ":"
        MOV     CX,4
        CALL    GETHX
        POP     AX
        JC      ADRERR
GETRET: CLC
        RET
SEGREG:
        MOV     AL,[SI]
        MOV     DI,OFFSET DG:SEGLET
        MOV     CX,4
        REPNE   SCASB
        JNZ     ADRERR
        INC     SI
        INC     SI
        SHL     CX,1
        MOV     BX,CX
        CMP     BYTE PTR [SI],":"
        JNZ     ADRERR
        PUSH    [BX+DSSAVE]
        JMP     SHORT GETDISP

SEGLET  DB      "CSED"

; Short form of ENTER command. A list of values from the
; command line are put into memory without using normal
; ENTER mode.

GETLIST:
        CALL    LIST                    ; Get the bytes to enter
        POP     DI                      ; Displacement within segment
        POP     ES                      ; Segment to enter into
        MOV     SI,OFFSET DG:BYTEBUF    ; List of bytes is in byte 2uffer
        MOV     CX,BX                   ; Count of bytes
        REP     MOVSB                   ; Enter that byte list
        RET

; Enter values into memory at a specified address. If the
; line contains nothing but the address we go into "enter
; mode", where the address and its current value are printed
; and the user may change it if desired. To change, type in
; new value in hex. Backspace works to correct errors. If
; an illegal hex digit or too many digits are typed, the
; bell is sounded but it is otherwise ignored. To go to the
; next byte (with or without change), hit space bar. To
; back   CLDto a previous address, type "-". On
; every 8-byte boundary a new line is started and the address
; is printed. To terminate command, type carriage return.
;   Alternatively, the list of bytes to be entered may be
; included on the original command line immediately following
; the address. This is in regular LIST format so any number
; of hex values or strings in quotes may be entered.

ENTER:
        MOV     BP,[DSSAVE]             ; Set default segment to DS
        CALL    ADDRESS
        PUSH    AX                      ; Save for later
        PUSH    DX
        CALL    SCANB                   ; Any more parameters?
        JNZ     GETLIST                 ; If not end-of-line get list
        POP     DI                      ; Displacement of ENTER
        POP     ES                      ; Segment
GETROW:
        CALL    OUTDI                   ; Print address of entry
        CALL    BLANK                   ; Leave a space
        CALL    BLANK
GETBYTE:
        MOV     AL,ES:[DI]              ; Get current value
        CALL    HEX                     ; And display it
PUTDOT:
        MOV     AL,"."
        CALL    OUT                     ; Prompt for new value
        MOV     CX,2                    ; Max of 2 digits in new value
        MOV     DX,0                    ; Intial new value
GETDIG:
        CALL    IN                      ; Get digit from user
        MOV     AH,AL                   ; Save
        CALL    HEXCHK                  ; Hex digit?
        XCHG    AH,AL                   ; Need original for echo
        JC      NOHEX                   ; If not, try special command
        MOV     DH,DL                   ; Rotate new value
        MOV     DL,AH                   ; And include new digit
        LOOP    GETDIG                  ; At most 2 digits
; We have two digits, so all we will accept now is a command.
DWAIT:
        CALL    IN                      ; Get command character
NOHEX:
        CMP     AL,8                    ; Backspace
        JZ      BS
        CMP     AL,7FH                  ; RUBOUT
        JZ      RUB
        CMP     AL,"-"                  ; Back   CLDto previous address
        JZ      PREV
        CMP     AL,13                   ; All done with command?
        JZ      EOL
        CMP     AL," "                  ; Go to next address
        JZ      NEXT
        MOV     AL,8
        CALL    OUT                     ; Back   CLDover illegal character
        CALL    BACKUP
        JCXZ    DWAIT
        JMP     SHORT GETDIG

RUB:
        MOV     AL,8
        CALL    OUT
BS:
        CMP     CL,2                    ; CX=2 means nothing typed yet
        JZ      PUTDOT                  ; Put back the dot we backed     CLDover
        INC     CL                      ; Accept one more character
        MOV     DL,DH                   ; Rotate out last digit
        MOV     DH,CH                   ; Zero this digit
        CALL    BACKUP                  ; Physical backspace
        JMP     SHORT GETDIG            ; Get more digits

; If new value has been entered, convert it to binary and
; put into memory. Always bump pointer to next location

STORE:
        CMP     CL,2                    ; CX=2 means nothing typed yet
        JZ      NOSTO                   ; So no new value to store
; Rotate DH left 4 bits to combine with DL and make a byte value
        PUSH    CX
        MOV     CL,4
        SHL     DH,CL
        POP     CX
        OR      DL,DH                   ; Hex is now converted to binary
        MOV     ES:[DI],DL              ; Store new value
NOSTO:
        INC     DI                      ; Prepare for next location
        RET
NEXT:
        CALL    STORE                   ; Enter new value
        INC     CX                      ; Leave a space plus two for
        INC     CX                      ;  each digit not entered
        CALL    TAB
        MOV     AX,DI                   ; Next memory address
        AND     AL,7                    ; Check for 8-byte boundary
        JNZ     GETBYTE                 ; Take 8 per line
NEWROW:
        CALL    CRLF                    ; Terminate line
        JMP     GETROW                  ; Print address on new line
PREV:
        CALL    STORE                   ; Enter the new value
; DI has been bumped to next byte. Drop it 2 to go to previous addr
        DEC     DI
        DEC     DI
        JMP     SHORT NEWROW            ; Terminate line after backing   CLD

EOL:
        CALL    STORE                   ; Enter the new value
        JMP     CRLF                    ; CR/LF and terminate

; Console input of single character

        IF      SYSVER
IN:
        PUSH    DS
        PUSH    SI
        LDS     SI,CS:[CIN]
        MOV     AH,4
        CALL    DEVIOCALL
        POP     SI
        POP     DS
        CMP     AL,3
        JNZ     NOTCNTC
        INT     23H
NOTCNTC:
        CMP     AL,'P'-'@'
        JZ      PRINTON
        CMP     AL,'N'-'@'
        JZ      PRINTOFF
        CALL    OUT
        RET

PRINTOFF:
PRINTON:
        NOT     [PFLAG]
        JMP     SHORT IN

        ELSE

IN:
        MOV     AH,1
        INT     21H
        RET
        ENDIF

CODE    ENDS
        END     DEBCOM1