Decoded: Rogue (1980) by Toy, Arnold, Wichman
DOS version (1983) by Mel Sibony and Jon Lane
Source file: DOS.ASM
Beginner friendly, line-by-line code walkthrough by MaiZure

DOS.ASM connects Rogue to the hardware via the operating system. These
procedures are the 'drivers' that rely on software interrupts and MMIO
ports to read and write data.

Original code:
https://britzl.github.io/roguearchive/

Original code with line numbers
http://www.maizure.org/projects/decoded-rogue/DOS_linenum.txt



1     COMMENT (recommended tab stop of 8)
2     COMMENT
3     COMMENT
4     COMMENT
5     BLANK
6     Start of the data segment, paragraph aligned.
7     Export tick variable to C source
8     Store two bytes for the tick value
9     Import the hit multiplier variable from EXTERN.C
10    Import the copy protection check from EXTERN.C 
11    Import the player name from EXTERN.C
12    Import the monster's name from EXTERN.C
13    Import the other player name variable from EXTERN.C
14    Import the print buffer pointer from INIT.C
15    Import the no_step variable from PROTECT.C
16    Import base pointer for code segment from Aztec C (not part of Rogue)
17    Import end pointer for code segment from Aztec C (not part of Rogue)
18    End of data segment
19    BLANK
20    Start of code segment, paragraph aligned.
21    Export DMA I/O, Break, beep, output, peek and poke procedures
22    Export character, memory, and clock procedures
23    Export segment, halt, and code check procedures
24    Export the quit procedure
25    Assume the usual for DS and CS. Other segments defined as needed
26    BLANK
27    COMMENT
28    COMMENT
29    COMMENT
30    COMMENT
31    COMMENT
32    COMMENT
33    BLANK
34    COMMENT
35    COMMENT
36    COMMENT
37    COMMENT
38    COMMENT
39    COMMENT
40    COMMENT
41    COMMENT
42    COMMENT
43    COMMENT
44    COMMENT
45    COMMENT
46    BLANK

OUTPUT TO MEMORY / PORT

47    Defines dmaout procedure to output bytes to an MMIO port
48    Save the current stack frame
49    Create a new stack frame
50    Store the destination index register
51    Store the source index register
52    Store the previous flags result
53    BLANK
54    Clear direction flag so that upcoming string ops read forward from SI/DI
55    Get the target segment in argument 3 for output in to AX
56    Store the original ES in BX for now
57    Move the target segment in to ES
58    Get the target offset from argument 4 in to DI
59    Get the source offset from argument 1 in to SI
60    Get the length counter from argument 2 in to CX
61    Repeatedly store words from source to destination until CX is 0
62    BLANK
63    Move the 0 that should be in CL to AH
64    Restore the original extra segment
65    BLANK
66    Restore the flags register
67    Restore the previous source index register
68    Restore the previous destination index register
69    Restore previous stack frame
70    Return to caller
71    End of dmaout procedure
72    BLANK

INPUT FROM MEMORY / PORT

73    Defines dmain procedure to read in bytes from an MMIO port
74    Save the current stack frame
75    Create a new stack frame
76    Store the destination index register
77    Store the source index register
78    Store the previous flags register
79    BLANK
80    Move the current data segment to AX (we can't directly move to ES)
81    Save the previous extra segment
82    Complete the move from DS to ES via AX
83    BLANK
84    Clear the direction flag to read/write forward from the index registers
85    Get the source segment to read from argument 3
86    Move that source to the data segment via AX
87    Move the companion source offset from argument 4 in to SI
88    Move the start variable offset from argument 1 in to DI
89    Move the size to read from argument 2 in to the counter register
90    Repeatedly read and store words until CX is 0
91    BLANK
92    Swap ES
93    and DS
94    Back to their original places 
95    BLANK
96  
97    BLANK
98    Restore the previous flags register
99    Restore the previous source index
100   Restore the previous destination index
101   Restore previous stack frame
102   Return to caller
103   End dmain procedure
104   BLANK

STORE VALUES IN MEMORY

105   Define wsetmem to push a value to a memory location (pre-memset memset)
106   Save the current stack frame
107   Create a new stack frame
108   Store the destination index
109   Store the source index
110   Store the previosu flags results
111   BLANK
112   Store the previous data segment because...
113   We'll pop it right in to the extra segment to use as the destination
114   BLANK
115   Move the target offset in to DI
116   Move the times to set in to CX  
117   Move the value to set in to AX
118   Clear direction flag to move forward from SI->DI
119   Repeat until CX expires or the value transferred is null
120   BLANK
121   Restore the previous flags register
122   Restore the previous source index
123   Restore the previous destination index
124   Restore previous stack frame
125   Return to caller
126   End wsetmem procedure
127   BLANK

*BEEP*

128   Defines beep to do just that
129   Save the current stack frame
130   Create a new stack frame
131   BLANK
132   Move 300 in to BX - this is how many cycles we're going to sound off
133   Read in the current state of the speaker (keyboard controller)
134   Save the state on the stack
135   Prepare the speaker bit (bit 1) to off
136   Send it to the speaker to turn it off
137   Set up a 50 tick counter in CX
138   Loop that counter to 0 to wait for speaker to move to off. This is
      takes approximately 50 / 5000000.
139   Now set the speaker to in (on)
140   Push that to the speaker
141   Set up delay for another 50 cycles
142   Now wait another 50 cycles. This really is a manual speaker pulse
143   Decrement the cycle counter in BX
144   If we still have more cycles to burn, jump back to line 135
145   Restore the original PC speaker port value in to AX
146   Push that back to the keyboard controller. Speaker now in old state
147   BLANK
148   Restore previous stack frame
149   Return to caller
150   End of beep procedure
151   BLANK
152   COMMENT
153   COMMENT
154   COMMENT
155   COMMENT
156   BLANK

NEW CTRL-BREAK HANDLER

157   Define ctrlbreak to replace the default DOS handler
158   Store the value in AX
159   Quit the game!
160   Restore the value in AX
161   This is an interrupt, so iret
162   BLANK

CLOCK HANDLER

163   Define clock...seems only to mess with people trying to hack the game
164   Disable interrupts
165   Store the old data segment
166   Store the old accumulator
167   COMMENT
168   Clear the accumulator
169   Move data segment to base memory page
170   Offset value at byte 4 in to AX (This is interrupt #1 - Trap/Step)
171   See if 0 was returned...
172   If so..fall through and die. OR jump to line 174
173   End this program
174   Try another trick...
175   Move a partial offset in to the IDT (between Int 1 and 2)
176   See if we have a zero...
177   If so...die, otherwise jump to line 179
178   End this program
179   We've passed two traps
180   COMMENT
181   Get the data segment...
182   And store it in its rightful place
183   Increment the tick counter
184   BLANK
185   COMMENT
186   Match the return values of no_step from PROTECT.C...  
187   If they match, we're almost done this proc. Jump to line 192
188   Push the function pointer forward?? Nice...
189   Check that value to see if it's less than 20...
190   If so...jump to line 192, otherwise..
191   Die!
192   Final check for copy protection
193   COMMENT
194   See if the hit multiplier is a nice and friendly 1x
195   If so, skip the copy protection down to line 203
196   Otherwise, check the copy protection result..
197   If it DOESNT match? Drop down and set the bad boy names
198   Get a blank space...
199   Store it in the killer's string
200   Get the player's name..
201   Store it in the global name (replacing the funny copy protection name)
202   Set the hit multiplier to 1x
203   Label to skip copy protection
204   Restore previous accumulator
205   Restore previous data segment
206   Return from interrupt
207   End clock procedure
208   BLANK
209   BLANK

SET NEW HANDLER FOR CTRL-BREAK

210   Define COFF to set up the new interrupt handler for ctrl-break
211   Get the offset of the handler in to DX
212   Save the old data segment
213   ...twice...
214   Save the code sgment
215   Move the code segment in to the data segment (handler is now data)
216   Prep to change interrupt vector. AH = 0x25, using number 0x23
217   BOOM goes the DOS interrupt - new ctrl-break handler is now in place
218   Restore the old data segment
219   Put the second copy in to the extra segment
220   Return
221   End of COFF procedure
222   BLANK

GET CHARACTER INPUT FROM KEYBOARD

223   Define getch to get a character from the keyboard
224   Set up to read keyboard buffer directly (AH=0)
225   Invoke the interrupt
226   Check to see if any value came back
227   If result is null, CMP al,0 means ZF=1. Real input means jump to 230
228   Otherwise, it's empty. Clear out the scan code
229   Assert bit 7 in the result
230   Clean the scan code...
231   Clear AH
232   Return to caller
233   End getch
234   BLANK

CHECK FOR CHARACTER

235   Define no_char to check if keyboard input is waiting
236   Set AH to 1
237   Invoke keyboard interrupt, ZF = 1 if keyboard is empty
238   Clear out the result
239   If there is something waiting....Jump to 241
240   Nothing is waiting...increment ax so that NO CHAR is true
241   Label for skipping
242   Return to caller
243   End of no char procedure
244   BLANK

OUTPUT TO MEMORY/PORT

245   Defines out to write value to memory/port
246   Get the current stack pointer (Args below the stack)
247   Get the port number in to DX
248   Get the byte to write in to AL
249   Output AL to DX
250   Return to caller
251   End of out procedure
252   BLANK

CHANGE A BYTE OF MEMORY (FAR)

253   Defines pokeb to set a far memory position to specific byte
254   Get the stack pointer
255   Store the data segment
256   Get the value to write in to al
257   Get the memory address in to bx using ds (DS:BX)
258   Move the value in al in to bx
259   Restore the original data segment
260   Return to caller
261   End of pokeb procedure
262   BLANK

READ A BYTE FROM MEMORY (FAR)

263   Defines peekb to read a byte from far memory
264   Get the stack pointer
265   Store the data segment
266   Get the memory value in both DS and BX
267   Move the memory value in to AL from DS:BX
268   Clear AH
269   Restore original data segment
270   Return value in AL
271   End of peek procedure
272   BLANK

FIND THE DATA SEGMENT

273   Defines getds to find the current data segment
274   Store the data segment
275   Pop it in to AX
276   Return to caller
277   End of getds procedure
278   BLANK

HALT AND CATCH FIRE

279   Define halt to sink this ship
280   Reverse memory read/write direction
281   Disable interrupts
282   Move the stack segment...
283   ...in to DS via AX
284   Move the stack pointer in to the destination index
285   Clear the accumulator
286   Prepare 10 writes...
287   Label for repeat
288   Start writing 0 backwards to smash some stack good
289   Count off to zero...
290   Repeat...
291   BLANK
292   May as well stop now
293   End of halt procedure
294   BLANK

FIND CODE SEGMENT

295   Define csum to find a specific point in the code segment (checksum!)
296   Move the beginning of the code segment addres in to AX
297   Move the end of the code segment in to BX
298   Set CX to 0...no loops
299   Store the previous source index
300   Store the previous data segment
301   Store the previous code segment
302   Pop the code segment in to the data segment
303   Move 0x200 bytes in to the code segment
304   Move this new position in to the source index
305   Move forward in memory
306   More code segment label
307   Load whatever byte happens to be at this position
308   Store this offset in to CX
309   Compare this position with the end of the code segment.
310   If there's more code segment...jump back to line 306 and read next byte
311   Otherwise, move our total offset in to AX
312   Restore the old data segment
313   Restore the old source index
314   Return the offset used as a checksum -- tada
315   End of csum procedure
316   BLANK
317   End of code segment
318   End of program
319   EOF