@@ -68,7 +68,7 @@ public void EvaluateOperands_MovAxBx_ShowsBothRegisterValues() {
6868 state . AX = 0x1234 ;
6969 state . BX = 0x5678 ;
7070
71- // x86 16-bit encoding: mov ax, bx → 89 D8
71+ // x86 16-bit encoding: mov ax, bx -> 89 D8
7272 byte [ ] machineCode = [ 0x89 , 0xD8 ] ;
7373 SegmentedAddress address = new ( 0x1000 , 0x0100 ) ;
7474 DebuggerLineViewModel line = CreateDebuggerLine ( machineCode , address ) ;
@@ -80,7 +80,8 @@ public void EvaluateOperands_MovAxBx_ShowsBothRegisterValues() {
8080
8181 // Assert
8282 evaluated . Should ( ) . NotBeNullOrEmpty ( ) ;
83- string text = SegmentsToText ( evaluated ! ) ;
83+ List < FormattedTextToken > evaluatedTokens = evaluated ?? [ ] ;
84+ string text = SegmentsToText ( evaluatedTokens ) ;
8485 text . Should ( ) . Contain ( "AX=0x1234" ) ;
8586 text . Should ( ) . Contain ( "BX=0x5678" ) ;
8687
@@ -107,7 +108,7 @@ public void EvaluateOperands_MovAxMemBx_ShowsMemoryValue() {
107108 // Write 0xABCD at physical address DS*16 + BX = 0x20000 + 0x50 = 0x20050
108109 memory . UInt16 [ 0x20050 ] = 0xABCD ;
109110
110- // x86 16-bit encoding: mov ax, word ptr [bx] → 8B 07
111+ // x86 16-bit encoding: mov ax, word ptr [bx] -> 8B 07
111112 byte [ ] machineCode = [ 0x8B , 0x07 ] ;
112113 SegmentedAddress address = new ( 0x1000 , 0x0100 ) ;
113114 DebuggerLineViewModel line = CreateDebuggerLine ( machineCode , address ) ;
@@ -119,7 +120,8 @@ public void EvaluateOperands_MovAxMemBx_ShowsMemoryValue() {
119120
120121 // Assert
121122 evaluated . Should ( ) . NotBeNullOrEmpty ( ) ;
122- string text = SegmentsToText ( evaluated ! ) ;
123+ List < FormattedTextToken > evaluatedTokens = evaluated ?? [ ] ;
124+ string text = SegmentsToText ( evaluatedTokens ) ;
123125 text . Should ( ) . Contain ( "AX=0x0000" ) ;
124126 text . Should ( ) . Contain ( "0xABCD" ) ;
125127
@@ -130,7 +132,7 @@ public void EvaluateOperands_MovAxMemBx_ShowsMemoryValue() {
130132
131133 /// <summary>
132134 /// Verifies that immediates are NOT evaluated (they are already visible in disassembly text).
133- /// ASM: mov ax, 0x1234 (opcode B8 34 12) — only AX should appear in evaluation.
135+ /// ASM: mov ax, 0x1234 (opcode B8 34 12) - only AX should appear in evaluation.
134136 /// </summary>
135137 [ AvaloniaFact ]
136138 public void EvaluateOperands_MovAxImm_OnlyShowsDestRegister ( ) {
@@ -140,7 +142,7 @@ public void EvaluateOperands_MovAxImm_OnlyShowsDestRegister() {
140142
141143 state . AX = 0x5555 ;
142144
143- // x86 16-bit encoding: mov ax, 0x1234 → B8 34 12
145+ // x86 16-bit encoding: mov ax, 0x1234 -> B8 34 12
144146 byte [ ] machineCode = [ 0xB8 , 0x34 , 0x12 ] ;
145147 SegmentedAddress address = new ( 0x1000 , 0x0100 ) ;
146148 DebuggerLineViewModel line = CreateDebuggerLine ( machineCode , address ) ;
@@ -152,7 +154,8 @@ public void EvaluateOperands_MovAxImm_OnlyShowsDestRegister() {
152154
153155 // Assert
154156 evaluated . Should ( ) . NotBeNullOrEmpty ( ) ;
155- string text = SegmentsToText ( evaluated ! ) ;
157+ List < FormattedTextToken > evaluatedTokens = evaluated ?? [ ] ;
158+ string text = SegmentsToText ( evaluatedTokens ) ;
156159 text . Should ( ) . Contain ( "AX=0x5555" ) ;
157160 // The immediate value 0x1234 should NOT appear in evaluation
158161 text . Should ( ) . NotContain ( "=0x1234" ) ;
@@ -175,7 +178,7 @@ public void EvaluateOperands_MovAxMemBxDisp_ShowsMemoryValue() {
175178 // Write 0xBEEF at physical address DS*16 + BX + 0x10 = 0x20000 + 0x50 + 0x10 = 0x20060
176179 memory . UInt16 [ 0x20060 ] = 0xBEEF ;
177180
178- // x86 16-bit encoding: mov ax, word ptr [bx+0x10] → 8B 47 10
181+ // x86 16-bit encoding: mov ax, word ptr [bx+0x10] -> 8B 47 10
179182 byte [ ] machineCode = [ 0x8B , 0x47 , 0x10 ] ;
180183 SegmentedAddress address = new ( 0x1000 , 0x0100 ) ;
181184 DebuggerLineViewModel line = CreateDebuggerLine ( machineCode , address ) ;
@@ -187,11 +190,162 @@ public void EvaluateOperands_MovAxMemBxDisp_ShowsMemoryValue() {
187190
188191 // Assert
189192 evaluated . Should ( ) . NotBeNullOrEmpty ( ) ;
190- string text = SegmentsToText ( evaluated ! ) ;
193+ List < FormattedTextToken > evaluatedTokens = evaluated ?? [ ] ;
194+ string text = SegmentsToText ( evaluatedTokens ) ;
191195 text . Should ( ) . Contain ( "AX=0x0000" ) ;
192196 text . Should ( ) . Contain ( "0xBEEF" ) ;
193197
194198 // Verify displacement operator is syntax-colored
195199 evaluated . Should ( ) . Contain ( s => s . Text == "+" && s . Kind == FormatterTextKind . Operator ) ;
196200 }
201+
202+ /// <summary>
203+ /// Verifies that LEA computes the effective address instead of reading memory.
204+ /// ASM: lea ax, [bp-8] (opcode 8D 46 F8) - should show computed address, not memory contents.
205+ /// </summary>
206+ [ AvaloniaFact ]
207+ public void EvaluateOperands_LeaAxBpMinus8_ShowsEffectiveAddress ( ) {
208+ // Arrange
209+ State state = CreateState ( ) ;
210+ ( Memory memory , _ , _ ) = CreateMemory ( ) ;
211+
212+ state . AX = 0x1111 ;
213+ state . BP = 0x0100 ;
214+ state . SS = 0x2000 ;
215+
216+ // Write decoy data at effective address - LEA must NOT read this
217+ uint physicalAddress = ( uint ) ( state . SS * 16 + state . BP - 8 ) ;
218+ memory . UInt16 [ physicalAddress ] = 0xDEAD ;
219+
220+ // x86 16-bit encoding: lea ax, [bp-8] -> 8D 46 F8
221+ byte [ ] machineCode = [ 0x8D , 0x46 , 0xF8 ] ;
222+ SegmentedAddress address = new ( 0x1000 , 0x0100 ) ;
223+ DebuggerLineViewModel line = CreateDebuggerLine ( machineCode , address ) ;
224+
225+ ExpressionEvaluationService service = new ( state , memory ) ;
226+
227+ // Act
228+ List < FormattedTextToken > ? evaluated = service . FormatOperandValues ( line . InstructionInfo ) ;
229+
230+ // Assert
231+ evaluated . Should ( ) . NotBeNullOrEmpty ( ) ;
232+ List < FormattedTextToken > evaluatedTokens = evaluated ?? [ ] ;
233+ string text = SegmentsToText ( evaluatedTokens ) ;
234+
235+ // LEA should show effective address = BP - 8 = 0x0100 - 8 = 0x00F8
236+ text . Should ( ) . Contain ( "0x00F8" ) ;
237+ // LEA must NOT show the memory contents 0xDEAD
238+ text . Should ( ) . NotContain ( "DEAD" ) ;
239+ // AX register value should still be shown
240+ text . Should ( ) . Contain ( "AX=0x1111" ) ;
241+ }
242+
243+ /// <summary>
244+ /// Verifies LEA with base+index computes the effective address.
245+ /// ASM: lea ax, [bx+si] (opcode 8D 00)
246+ /// </summary>
247+ [ AvaloniaFact ]
248+ public void EvaluateOperands_LeaAxBxSi_ShowsEffectiveAddress ( ) {
249+ // Arrange
250+ State state = CreateState ( ) ;
251+ ( Memory memory , _ , _ ) = CreateMemory ( ) ;
252+
253+ state . AX = 0x0000 ;
254+ state . BX = 0x0030 ;
255+ state . SI = 0x0010 ;
256+ state . DS = 0x2000 ;
257+
258+ // x86 16-bit encoding: lea ax, [bx+si] -> 8D 00
259+ byte [ ] machineCode = [ 0x8D , 0x00 ] ;
260+ SegmentedAddress address = new ( 0x1000 , 0x0100 ) ;
261+ DebuggerLineViewModel line = CreateDebuggerLine ( machineCode , address ) ;
262+
263+ ExpressionEvaluationService service = new ( state , memory ) ;
264+
265+ // Act
266+ List < FormattedTextToken > ? evaluated = service . FormatOperandValues ( line . InstructionInfo ) ;
267+
268+ // Assert
269+ evaluated . Should ( ) . NotBeNullOrEmpty ( ) ;
270+ List < FormattedTextToken > evaluatedTokens = evaluated ?? [ ] ;
271+ string text = SegmentsToText ( evaluatedTokens ) ;
272+ // Effective address = BX + SI = 0x0030 + 0x0010 = 0x0040
273+ text . Should ( ) . Contain ( "0x0040" ) ;
274+ }
275+
276+ /// <summary>
277+ /// Verifies that LDS evaluates the far pointer memory operand (dword containing offset:segment).
278+ /// ASM: lds si, ss:[bp+0x10] (opcode C5 76 10) - should show the dword value at memory.
279+ /// </summary>
280+ [ AvaloniaFact ]
281+ public void EvaluateOperands_LdsSiBpPlus10_ShowsFarPointerValue ( ) {
282+ // Arrange
283+ State state = CreateState ( ) ;
284+ ( Memory memory , _ , _ ) = CreateMemory ( ) ;
285+
286+ state . SI = 0x0000 ;
287+ state . BP = 0x0984 ;
288+ state . SS = 0x2F23 ;
289+ state . DS = 0x261F ;
290+
291+ // Write a far pointer (offset:segment) at SS:BP+0x10
292+ uint physicalAddress = ( uint ) ( state . SS * 16 + state . BP + 0x10 ) ;
293+ memory . UInt16 [ physicalAddress ] = 0x1234 ; // offset
294+ memory . UInt16 [ physicalAddress + 2 ] = 0x5678 ; // segment
295+
296+ // x86 16-bit encoding: lds si, [bp+0x10] -> C5 76 10
297+ byte [ ] machineCode = [ 0xC5 , 0x76 , 0x10 ] ;
298+ SegmentedAddress address = new ( 0x1000 , 0x0100 ) ;
299+ DebuggerLineViewModel line = CreateDebuggerLine ( machineCode , address ) ;
300+
301+ ExpressionEvaluationService service = new ( state , memory ) ;
302+
303+ // Act
304+ List < FormattedTextToken > ? evaluated = service . FormatOperandValues ( line . InstructionInfo ) ;
305+
306+ // Assert
307+ evaluated . Should ( ) . NotBeNullOrEmpty ( ) ;
308+ List < FormattedTextToken > evaluatedTokens = evaluated ?? [ ] ;
309+ string text = SegmentsToText ( evaluatedTokens ) ;
310+ // Should show the dword value read from memory (segment:offset packed as dword)
311+ text . Should ( ) . Contain ( "0x56781234" ) ;
312+ // SI register value should also be shown
313+ text . Should ( ) . Contain ( "SI=0x0000" ) ;
314+ }
315+
316+ /// <summary>
317+ /// Verifies that CALL dword ptr [mem] evaluates the far pointer memory operand.
318+ /// ASM: call dword ptr ss:[bp-4] (opcode FF 5E FC) - should show the target address.
319+ /// </summary>
320+ [ AvaloniaFact ]
321+ public void EvaluateOperands_CallFarPtrBpMinus4_ShowsFarPointerValue ( ) {
322+ // Arrange
323+ State state = CreateState ( ) ;
324+ ( Memory memory , _ , _ ) = CreateMemory ( ) ;
325+
326+ state . BP = 0x0984 ;
327+ state . SS = 0x2F23 ;
328+
329+ // Write a far pointer (offset:segment) at SS:BP-4
330+ uint physicalAddress = ( uint ) ( state . SS * 16 + state . BP - 4 ) ;
331+ memory . UInt16 [ physicalAddress ] = 0xABCD ; // offset
332+ memory . UInt16 [ physicalAddress + 2 ] = 0x1234 ; // segment
333+
334+ // x86 16-bit encoding: call dword ptr [bp-4] -> FF 5E FC
335+ byte [ ] machineCode = [ 0xFF , 0x5E , 0xFC ] ;
336+ SegmentedAddress address = new ( 0x1000 , 0x0100 ) ;
337+ DebuggerLineViewModel line = CreateDebuggerLine ( machineCode , address ) ;
338+
339+ ExpressionEvaluationService service = new ( state , memory ) ;
340+
341+ // Act
342+ List < FormattedTextToken > ? evaluated = service . FormatOperandValues ( line . InstructionInfo ) ;
343+
344+ // Assert
345+ evaluated . Should ( ) . NotBeNullOrEmpty ( ) ;
346+ List < FormattedTextToken > evaluatedTokens = evaluated ?? [ ] ;
347+ string text = SegmentsToText ( evaluatedTokens ) ;
348+ // Should show the dword value read from memory
349+ text . Should ( ) . Contain ( "0x1234ABCD" ) ;
350+ }
197351}
0 commit comments