[Vm-dev] VM Maker: GDB-bgs.1.mcz

commits at source.squeak.org commits at source.squeak.org
Sun Apr 12 23:54:28 UTC 2020


Boris Shingarov uploaded a new version of GDB to project VM Maker:
http://source.squeak.org/VMMaker/GDB-bgs.1.mcz

==================== Summary ====================

Name: GDB-bgs.1
Author: bgs
Time: 12 April 2020, 7:54:25.444451 pm
UUID: 0eba5e72-c35a-4d8c-a192-489728067891
Ancestors: 

Initial import of GDB Remote Client interface

==================== Snapshot ====================

SystemOrganization addCategory: #'GDB-RSP'!
SystemOrganization addCategory: #'GDB-TAJ'!
SystemOrganization addCategory: #'GDB-UI'!
SystemOrganization addCategory: #'GDB-Primitives'!
SystemOrganization addCategory: #'GDB-Tests'!
SystemOrganization addCategory: #'GDB-Doodles'!
SystemOrganization addCategory: #'GDB-Cog'!

BorderedMorph subclass: #GdbMTEngineMorph
	instanceVariableNames: 'gdb regs'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-UI'!

!GdbMTEngineMorph commentStamp: 'BorisShingarov 4/25/2016 13:39' prior: 0!
I add a 'Modtalk' tab to the GT Inspector.!

----- Method: GdbMTEngineMorph class>>on: (in category 'instance creation') -----
on: aGDB
	^self new
		gdb: aGDB;
		yourself!

----- Method: GdbMTEngineMorph>>defaultBounds (in category 'as yet unclassified') -----
defaultBounds
	"Answer the default bounds for the receiver."

	^0 @ 0 corner: 500 @ 300!

----- Method: GdbMTEngineMorph>>drawCurrentBytecodeOn:fromHeight: (in category 'as yet unclassified') -----
drawCurrentBytecodeOn: clippedCanvas fromHeight: y
	| bc |
	bc := [ gdb currentBytecode printString ]
		on: Error do: [ '???' ].
	clippedCanvas
		drawString: bc
		at: (self bounds topLeft + (0 @ y))
		font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 12)
		color: Color black.
	^y + 14!

----- Method: GdbMTEngineMorph>>drawCurrentFrameOn:fromHeight: (in category 'as yet unclassified') -----
drawCurrentFrameOn: clippedCanvas fromHeight: y
	| fp  |
	fp := gdb getVRegister: #FP.
	self drawFrame: fp withAllSendersOn: clippedCanvas fromHeight: y
!

----- Method: GdbMTEngineMorph>>drawCurrentInstructionOn:fromHeight: (in category 'as yet unclassified') -----
drawCurrentInstructionOn: clippedCanvas fromHeight: y
	| pc nativeInstr |
	pc := regs at: 'pc'.
	nativeInstr := gdb currentInstruction.
	clippedCanvas
		drawString: nativeInstr printString
		at: (self bounds topLeft + (0 @ y))
		font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 12)
		color: Color black.
	^y + 14!

----- Method: GdbMTEngineMorph>>drawFrame:on:fromHeight: (in category 'as yet unclassified') -----
drawFrame: fp on: clippedCanvas fromHeight: y
	| frame cm |
	frame := MTRemoteStackFrame gdb: gdb pointer: fp.
	cm := frame method.
	clippedCanvas
		drawString: cm selector symbol asString
		at: (self bounds topLeft + (0 @ y))
		font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 12)
		color: Color black.
	^y+20!

----- Method: GdbMTEngineMorph>>drawFrame:withAllSendersOn:fromHeight: (in category 'as yet unclassified') -----
drawFrame: fp withAllSendersOn: clippedCanvas fromHeight: y
	| frame yy |
	yy := self drawFrame: fp on: clippedCanvas fromHeight: y.
	frame := MTRemoteStackFrame gdb: gdb pointer: fp.
	frame .
	^y+20
!

----- Method: GdbMTEngineMorph>>drawOn: (in category 'as yet unclassified') -----
drawOn: aCanvas
	regs := gdb getRegisters.
	aCanvas
		clipBy: self bounds
		during: [:clippedCanvas |
			clippedCanvas
				fillRectangle: self bounds
				color: Color white.	
		self drawCurrentFrameOn: clippedCanvas fromHeight:
		(self drawCurrentBytecodeOn: clippedCanvas fromHeight:
		(self drawCurrentInstructionOn: clippedCanvas fromHeight:
		(self drawVregsOn: clippedCanvas) + 15)) + 15
				].
 !

----- Method: GdbMTEngineMorph>>drawSPR:on:fromHeight: (in category 'as yet unclassified') -----
drawSPR: spr on: clippedCanvas fromHeight: y
	clippedCanvas
		drawString: spr, ' = ', (regs at: spr) printString
		at: (self bounds topLeft + (0 @ y))
		font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 12)
		color: Color black.
	^y + 14	!

----- Method: GdbMTEngineMorph>>drawSPRsOn:fromHeight: (in category 'as yet unclassified') -----
drawSPRsOn: clippedCanvas fromHeight: y
	| yy |
	yy := y.
	#('pc' 'lr' 'cr' 'ctr' 'msr' 'xer') do: [ :spr |
		self drawSPR: spr on: clippedCanvas fromHeight: yy.
		yy := yy + 14.
		].
	^yy!

----- Method: GdbMTEngineMorph>>drawVRegHeaderOn: (in category 'as yet unclassified') -----
drawVRegHeaderOn: clippedCanvas
	clippedCanvas
		drawString: 'VRegs:'
		at: self bounds topLeft 
		font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 14)
		color: Color black.
	^18 "BOGUS CODE"
	!

----- Method: GdbMTEngineMorph>>drawVregsOn: (in category 'as yet unclassified') -----
drawVregsOn: clippedCanvas
	| y |
	y := self drawVRegHeaderOn: clippedCanvas.
	TAJWriter registerMap keysAndValuesDo: [ :vReg :physReg |
		| regName |
		regName := physReg isInteger ifTrue: ['r', physReg printString]
			ifFalse: [ physReg ].

		clippedCanvas
			drawString: vReg printString, ' = ', (regs at: regName) printString
			at: (self bounds topLeft + (0 @ y))
			font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 12)
			color: Color black.
		y := y + 14.
		 ].
	^y
	!

----- Method: GdbMTEngineMorph>>gdb (in category 'accessing') -----
gdb
	^ gdb!

----- Method: GdbMTEngineMorph>>gdb: (in category 'accessing') -----
gdb: anObject
	gdb := anObject!

BorderedMorph subclass: #GdbRegistersMorph
	instanceVariableNames: 'gdb regs'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-UI'!

!GdbRegistersMorph commentStamp: 'BorisShingarov 4/25/2016 13:39' prior: 0!
I add a 'Registers' tab to the GT Inspector.!

----- Method: GdbRegistersMorph class>>concreteClassFor: (in category 'instance creation') -----
concreteClassFor: aGDB
	^Smalltalk classNamed: 'GdbRegistersMorph', aGDB processorDescription architectureName !

----- Method: GdbRegistersMorph class>>on: (in category 'instance creation') -----
on: aGDB
	^(self concreteClassFor: aGDB) new
		gdb: aGDB;
		yourself!

----- Method: GdbRegistersMorph>>defaultBounds (in category 'drawing') -----
defaultBounds
	"Answer the default bounds for the receiver."

	^0 @ 0 corner: 500 @ 300!

----- Method: GdbRegistersMorph>>drawGPRHeaderOn: (in category 'drawing') -----
drawGPRHeaderOn: clippedCanvas
	clippedCanvas
		drawString: 'GPR:'
		at: self bounds topLeft 
		font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 14)
		color: Color black.
	^18 "BOGUS CODE"
	!

----- Method: GdbRegistersMorph>>drawGPRsOn: (in category 'drawing') -----
drawGPRsOn: clippedCanvas
	| y |
	y := self drawGPRHeaderOn: clippedCanvas.
	0 to: 31 do: [ :r |
		| regName |
		regName := 'r', r printString.
		clippedCanvas
			drawString: regName, ' = ', (regs at: regName) printString
			at: (self bounds topLeft + (0 @ y))
			font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 12)
			color: Color black.
		y := y + 15.
		 ].
	^y
	!

----- Method: GdbRegistersMorph>>drawOn: (in category 'drawing') -----
drawOn: aCanvas
	regs := gdb getRegisters.
	aCanvas
		clipBy: self bounds
		during: [:clippedCanvas |
			clippedCanvas
				fillRectangle: self bounds
				color: Color yellow.	
			self drawRegistersOn: clippedCanvas
		]
 !

----- Method: GdbRegistersMorph>>gdb (in category 'accessing') -----
gdb
	^ gdb!

----- Method: GdbRegistersMorph>>gdb: (in category 'accessing') -----
gdb: anObject
	gdb := anObject!

GdbRegistersMorph subclass: #GdbRegistersMorphIA32
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-UI'!

----- Method: GdbRegistersMorphIA32>>drawRegistersOn: (in category 'drawing') -----
drawRegistersOn: clippedCanvas
	| y |
	y := self drawGPRHeaderOn: clippedCanvas.
	gdb processorDescription gdb do: [ :r |
		| regName |
		regName := r regName.
		clippedCanvas
			drawString: regName, ' = ', (regs at: regName) printString
			at: (self bounds topLeft + (0 @ y))
			font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 12)
			color: Color black.
		y := y + 15.
		 ].
	^y
	!

GdbRegistersMorph subclass: #GdbRegistersMorphpowerpc
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-UI'!

----- Method: GdbRegistersMorphpowerpc>>drawRegistersOn: (in category 'drawing') -----
drawRegistersOn: clippedCanvas
	self drawSPRsOn: clippedCanvas fromHeight:
		(self drawGPRsOn: clippedCanvas) + 15
 !

----- Method: GdbRegistersMorphpowerpc>>drawSPR:on:fromHeight: (in category 'drawing') -----
drawSPR: spr on: clippedCanvas fromHeight: y
	clippedCanvas
		drawString: spr, ' = ', (regs at: spr) printString
		at: (self bounds topLeft + (0 @ y))
		font: (LogicalFont familyName: 'Bitmap DejaVu Sans' pointSize: 12)
		color: Color black.
	^y + 14	!

----- Method: GdbRegistersMorphpowerpc>>drawSPRsOn:fromHeight: (in category 'drawing') -----
drawSPRsOn: clippedCanvas fromHeight: y
	| yy |
	yy := y.
	self sprNames  do: [ :spr |
		self drawSPR: spr on: clippedCanvas fromHeight: yy.
		yy := yy + 15.
		]!

----- Method: GdbRegistersMorphpowerpc>>sprNames (in category 'drawing') -----
sprNames
	gdb processorDescription architectureName = 'powerpc' 
		ifTrue: [ ^#('pc' 'lr' 'cr' 'ctr' 'msr' 'xer') ].
	^#('pc' 'sr' 'hi' 'lo' 'cause' 'badvaddr')!

Error subclass: #GdbChildExited
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

Error subclass: #InferiorExited
	instanceVariableNames: 'exitCode'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: InferiorExited class>>exitCode: (in category 'instance creation') -----
exitCode: anInteger
	^self new exitCode: anInteger; yourself!

----- Method: InferiorExited class>>signalWithExitCode: (in category 'signaling') -----
signalWithExitCode: anInteger

	^(self exitCode: anInteger) signal!

----- Method: InferiorExited>>exitCode (in category 'accessing') -----
exitCode
	^ exitCode!

----- Method: InferiorExited>>exitCode: (in category 'accessing') -----
exitCode: anObject
	exitCode := anObject!

TestCase subclass: #DebugStoppedTestCase
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Tests'!

----- Method: DebugStoppedTestCase>>testCreateSEGV (in category 'tests-basic') -----
testCreateSEGV
	| sig |
	sig := DebugStopped onSignalNum: 11.
	self assert: sig signal equals: #SIGSEGV!

----- Method: DebugStoppedTestCase>>testCreateSYS (in category 'tests-basic') -----
testCreateSYS
	| sig |
	sig := DebugStopped onSignalNum: 31.
	self assert: sig signal equals: #SIGSYS!

----- Method: DebugStoppedTestCase>>testCreateTRAP (in category 'tests-basic') -----
testCreateTRAP
	| sig |
	sig := DebugStopped onSignalNum: 5.
	self assert: sig signal equals: #SIGTRAP!

TestCase subclass: #FeatureParserTestCase
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Tests'!

----- Method: FeatureParserTestCase>>testParse1 (in category 'tests') -----
testParse1
	| regs r1 lr ctr |
	regs := (GdbXmlParser endian: FakeProcessorDescriptionPPC endian)
		parseString: FakeProcessorDescriptionPPC features.
	r1 := regs at: 2.
	self assert: r1 regName        equals: 'r1'.
	self assert: r1 width          equals: 32.
	self assert: r1 isLittleEndian equals: false.
	self assert: r1 regNum         equals: 1.
	
	lr := regs at: 68.
	self assert: lr regName        equals: 'lr'.
	self assert: lr width          equals: 32.
	self assert: lr isLittleEndian equals: false.
	self assert: lr regNum         equals: 67.
	
	ctr := regs at: 69.
	self assert: ctr regName        equals: 'ctr'.
	self assert: ctr width          equals: 32.
	self assert: ctr isLittleEndian equals: false.
	self assert: ctr regNum         equals: 68.
!

TestCase subclass: #GDBSocketTimeoutTest
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Tests'!

----- Method: GDBSocketTimeoutTest>>connectGdb (in category 'as yet unclassified') -----
connectGdb
	^self debuggerClass
		host: self hostIP
		port: 7000
		processorDescription: FakeProcessorDescriptionPPC new!

----- Method: GDBSocketTimeoutTest>>hostIP (in category 'as yet unclassified') -----
hostIP
	^'192.168.75.2'!

----- Method: GDBSocketTimeoutTest>>testBad (in category 'as yet unclassified') -----
testBad
	| gdb |
	gdb := self connectGdb.
	gdb halt.
	self should: [ gdb c ] raise: ConnectionClosed
!

----- Method: GDBSocketTimeoutTest>>testGood (in category 'as yet unclassified') -----
testGood
	| gdb |
	gdb := self connectGdb.
	gdb inspect!

TestCase subclass: #RemoteGDBTestCase
	instanceVariableNames: 'gdb pdl'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

!RemoteGDBTestCase commentStamp: 'bgs 3/22/2020 00:16' prior: 0!
I am deprecated leftover from TA-MT.
!

----- Method: RemoteGDBTestCase>>connectGDB (in category 'as yet unclassified') -----
connectGDB
	pdl := AcProcessorDescriptions powerpc.
	gdb := RemoteGDBSession
		host: '192.168.75.2'
		port: 7000
		processorDescription: pdl.!

----- Method: RemoteGDBTestCase>>exitSyscall: (in category 'syscall sequences') -----
exitSyscall: rtnInt
	| loadSyscallNo loadReturnValue sc |
	loadSyscallNo := (pdl instructionAt: #addi) bind: (Dictionary new
		at: 'ra' put: 0;
		at: 'rt' put: 0;
		at: 'd' put: 1;
		yourself).
	loadReturnValue := (pdl instructionAt: #addi) bind: (Dictionary new
		at: 'ra' put: 0;
		at: 'rt' put: 3;
		at: 'd' put: rtnInt;
		yourself).
	sc := (pdl instructionAt: #sc) bind: (Dictionary new
		at: 'lev' put: 0;
		yourself).
	^(Array with: loadSyscallNo with: loadReturnValue with: sc)
	collect: [ :instr | instr emit ]!

----- Method: RemoteGDBTestCase>>messageBytes (in category 'syscall sequences') -----
messageBytes
	^'HELLO!!' asByteArray, #(10 0)!

----- Method: RemoteGDBTestCase>>testManualSyscallInNZone (in category 'as yet unclassified') -----
testManualSyscallInNZone
	| memLayout writeInstructions exitInstructions |
	self halt.
	self connectGDB.
	memLayout := ThinshellAddressLayout gdb: gdb.
	memLayout executeStartChain.
	writeInstructions := self writeSyscall: self messageBytes.
	exitInstructions := self exitSyscall: 1.
	gdb writeInt32s: writeInstructions, exitInstructions toAddr: memLayout nZone.
	gdb writeBytes: self messageBytes toAddr: memLayout heap.
	
	gdb
		stepUntil: [ gdb currentInstruction name = 'sc' ];
		s.   "the actual write syscall"
	
	gdb s; s; s. "exit"
	Transcript yourself
	!

----- Method: RemoteGDBTestCase>>testSetRegisters (in category 'as yet unclassified') -----
testSetRegisters

	| regs1 regs2 |
	self halt.
	regs1 := gdb getRegisters.
	gdb setRegisters: regs1.
	regs2 := gdb getRegisters.
	self assert: regs1 = regs2.!

----- Method: RemoteGDBTestCase>>writeSyscall: (in category 'syscall sequences') -----
writeSyscall: aByteArray
	| loadSyscallNo loadFD loadBuf loadLength sc |
	loadSyscallNo := (pdl instructionAt: #addi) bind: (Dictionary new
		at: 'ra' put: 0;
		at: 'rt' put: 0;
		at: 'd' put: 4;
		yourself).
	loadFD := (pdl instructionAt: #addi) bind: (Dictionary new
		at: 'ra' put: 0;
		at: 'rt' put: 3;
		at: 'd' put: 1;
		yourself).
	loadBuf := (pdl instructionAt: #addi) bind: (Dictionary new
		at: 'ra' put: 17;
		at: 'rt' put: 4;
		at: 'd' put: 0;
		yourself).
	loadLength := (pdl instructionAt: #addi) bind: (Dictionary new
		at: 'ra' put: 0;
		at: 'rt' put: 5;
		at: 'd' put: (aByteArray size);
		yourself).
	sc := (pdl instructionAt: #sc) bind: (Dictionary new
		at: 'lev' put: 0;
		yourself).
	^(Array with: loadSyscallNo with: loadFD with: loadBuf with: loadLength with: sc)
	collect: [ :instr | instr emit ]!

Object subclass: #AddressSpaceLayout
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-TAJ'!

!AddressSpaceLayout commentStamp: 'BorisShingarov 3/7/2020 13:22' prior: 0!
In Target-Agnostic Modtalk, when we are AoT-ing the binary image, somewhere in the address space there is the arena where we construct the object heap, and the nZone.  Instances of my concrete subclasses know where these two addresses are.!

----- Method: AddressSpaceLayout>>heap (in category 'accessing') -----
heap
	self subclassResponsibility !

----- Method: AddressSpaceLayout>>nZone (in category 'accessing') -----
nZone
	self subclassResponsibility !

AddressSpaceLayout subclass: #ThinshellAddressLayout
	instanceVariableNames: 'gdb nZone heap stack'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-TAJ'!

!ThinshellAddressLayout commentStamp: 'BorisShingarov 3/7/2020 14:02' prior: 0!
The TAM Thinshell, as described in the Kilpela-Shingarov report, and available in the shingarov/thinshell GitHub repo, is an ELF binary which doesn't link to the C runtime.  Its simple, processor-specific assembly source declares areas for heap, nZone and stack, and a _start entry point leading into a "start chain".  The start chain has a few instructions to load the addresses of the heap, nZone and stack into register defined by convention, a magic-point for transfering control to the outer-Smalltalk ProgramBuilder, and a jump to the beginning of the nZone.!

----- Method: ThinshellAddressLayout class>>gdb: (in category 'instance creation') -----
gdb: aGDB
	^self basicNew
		gdb: aGDB;
		initialize!

----- Method: ThinshellAddressLayout class>>registerAssignments (in category 'register conventions') -----
registerAssignments
	"This is TAM-specific and probably should not be here."
	^
	(#R  -> 1),
	(#A  -> 2),
	(#A  -> 3),
	(#FP -> 4),

	(#Scratch1  -> 5),
	(#Scratch2  -> 6),
	(#Scratch3  -> 7),
	(#Scratch4  -> 8),
	(#Scratch5  -> 9),
	(#Scratch6  -> 10),
	(#Scratch7  -> 11),
	(#Scratch8  -> 12),
	(#Scratch9  -> 13),
	(#Scratch10 -> 14),
	
	(#NZone -> 16),
	(#HEAP  -> 2),
	(#SP    -> 18),
	(#VPC   -> 19),
	
	(#NativePC -> 'pc')
	!

----- Method: ThinshellAddressLayout>>executeStartChain (in category 'initialization') -----
executeStartChain
	| regs |
	regs := gdb s; "the first nop"
		s; s; "lis/ori 16"
		s; s;
		s; s;
		s; s; "2 nops"
		getRegisters.

	nZone := regs at: (self regNameFor: #NZone).
	heap := regs at: (self regNameFor: #HEAP).
	stack := regs at: (self regNameFor: #SP).!

----- Method: ThinshellAddressLayout>>gdb (in category 'accessing') -----
gdb
	^ gdb!

----- Method: ThinshellAddressLayout>>gdb: (in category 'accessing') -----
gdb: anObject
	gdb := anObject!

----- Method: ThinshellAddressLayout>>heap (in category 'accessing') -----
heap
	^ heap!

----- Method: ThinshellAddressLayout>>heap: (in category 'accessing') -----
heap: anObject
	heap := anObject!

----- Method: ThinshellAddressLayout>>initialize (in category 'initialization') -----
initialize
	super initialize.
	self executeStartChain!

----- Method: ThinshellAddressLayout>>nZone (in category 'accessing') -----
nZone
	^ nZone!

----- Method: ThinshellAddressLayout>>nZone: (in category 'accessing') -----
nZone: anObject
	nZone := anObject!

----- Method: ThinshellAddressLayout>>regNameFor: (in category 'cooperation with gdb') -----
regNameFor: vRegName
	^'r', (ThinshellAddressLayout registerAssignments at: vRegName) printString!

----- Method: ThinshellAddressLayout>>stack (in category 'accessing') -----
stack
	^ stack!

----- Method: ThinshellAddressLayout>>stack: (in category 'accessing') -----
stack: anObject
	stack := anObject!

Object subclass: #BasePrimitiveProcessor
	instanceVariableNames: 'gdb regs'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Primitives'!

----- Method: BasePrimitiveProcessor class>>gdb: (in category 'as yet unclassified') -----
gdb: gdb
	^self new
		gdb: gdb;
		yourself!

----- Method: BasePrimitiveProcessor>>advancePastTrap (in category 'engine') -----
advancePastTrap
	regs at: 'pc' put: (regs at: 'pc')+4!

----- Method: BasePrimitiveProcessor>>allocIndexed:md: (in category 'allocating') -----
allocIndexed: size md: md
	|  ptr obj liveObj mtNil |
	ptr := self allocSlots: size serviceSlots: 3.
	obj := MTRemoteObject gdb: gdb pointer: ptr.
	obj header: (TAJObjectWriter
		declareObjectHeaderType: 'IndexedObjectType'
		hashFormat: 'NoHash'
		numVars: size
		hash: 0
		meta: 0).
	obj md: md.
	liveObj := MTRemoteLiveIndexedObject gdb: gdb pointer: ptr.
	mtNil := gdb exe externalReferences at: #MT_nil.
	1 to: size do: [ :i |
		liveObj mtAt: i put: mtNil ].
	self return: ptr!

----- Method: BasePrimitiveProcessor>>allocOop:md: (in category 'allocating') -----
allocOop: numVars md: md
	|  ptr obj mtNil |
	ptr := self allocSlots: numVars serviceSlots: 3 "header, md, hash".
	obj := MTRemoteObject gdb: gdb pointer: ptr.
	obj header: (TAJObjectWriter
		declareObjectHeaderType: 'OopObjectType'
		hashFormat: 'NoHash'
		numVars: numVars hash: 0 meta: 0).
	obj md: md.
	"nil the slots:"
	mtNil := gdb exe externalReferences at: #MT_nil.
	1 to: numVars do: [ :idx | obj basicSlotAt: idx+2 put: mtNil ].
	^ptr
!

----- Method: BasePrimitiveProcessor>>allocSlots:serviceSlots: (in category 'allocating') -----
allocSlots: numSlots serviceSlots: s
	| nBytes p |
	false ifTrue: [ ^self allocSlotsFromMTXMemory: numSlots ].
	
	nBytes := (numSlots + s) * 4.
	nBytes := nBytes+15 bitAnd: 16rFFFFFFF0.
	p := self getVRegister: #HEAP.
	self setVRegister: #HEAP to: p + nBytes.
	^p!

----- Method: BasePrimitiveProcessor>>allocSlotsFromMTXMemory: (in category 'allocating') -----
allocSlotsFromMTXMemory: numSlots
	| nBytes |
	nBytes := numSlots * 4.
	^self gdb exe objectMemory alloc: nBytes!

----- Method: BasePrimitiveProcessor>>allocString:md: (in category 'allocating') -----
allocString: size md: md
	| ptr obj |
	ptr := self allocSlots: (size + 2 + 3 // 4) serviceSlots: 3 "header, md, hash".
	obj := MTRemoteObject gdb: gdb pointer: ptr.
	obj header: (TAJObjectWriter
		declareObjectHeaderType: 'ZByteObjectType'
		hashFormat: 'StringHash'
		numVars: size
		hash: 0
		meta: 0).
	obj md: md.
	self return: ptr!

----- Method: BasePrimitiveProcessor>>currentFrame (in category 'engine') -----
currentFrame
"NB -- this is problematic, this parallel implementation with GDB"
	^MTRemoteStackFrame gdb: gdb pointer: (self getVRegister: #FP)!

----- Method: BasePrimitiveProcessor>>gdb (in category 'accessing') -----
gdb
	^ gdb!

----- Method: BasePrimitiveProcessor>>gdb: (in category 'accessing') -----
gdb: anObject
	gdb := anObject!

----- Method: BasePrimitiveProcessor>>getAllRegisters (in category 'engine') -----
getAllRegisters
	regs := gdb getRegisters!

----- Method: BasePrimitiveProcessor>>getRegister: (in category 'engine') -----
getRegister: r
	^regs at: r!

----- Method: BasePrimitiveProcessor>>getVRegister: (in category 'engine') -----
getVRegister: regSym
	| nRegister |
	nRegister := TAJWriter vRegister: regSym.
	^self getRegister: 'r', nRegister printString!

----- Method: BasePrimitiveProcessor>>processPrimitive: (in category 'engine') -----
processPrimitive: primitiveSelector
	self
		getAllRegisters;
		perform: primitiveSelector;
		advancePastTrap;
		setAllRegisters!

----- Method: BasePrimitiveProcessor>>setAllRegisters (in category 'engine') -----
setAllRegisters
	gdb setRegisters: regs!

----- Method: BasePrimitiveProcessor>>setRegister:to: (in category 'engine') -----
setRegister: r to: x
	regs at: r put: x!

----- Method: BasePrimitiveProcessor>>setVRegister:to: (in category 'engine') -----
setVRegister: r to: x
	| nRegister |
	nRegister := TAJWriter vRegister:  r.
	nRegister := 'r', nRegister printString.
	self setRegister: nRegister to: x!

BasePrimitiveProcessor subclass: #PrimitiveProcessor
	instanceVariableNames: 'exceptionEnvSlot'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Primitives'!

!PrimitiveProcessor commentStamp: 'BorisShingarov 5/5/2016 17:24' prior: 0!
When the native runtime does not have a native implementation for a primitive, a callback into the outer Smalltalk happens which I then process, surgically operating on the state of the inner VM.!

----- Method: PrimitiveProcessor>>falseObject (in category 'accessing') -----
falseObject
	^gdb exe externalReferences at: #MT_false!

----- Method: PrimitiveProcessor>>getExceptionEnvSlot (in category 'accessing') -----
getExceptionEnvSlot 
	exceptionEnvSlot isNil ifTrue: [ exceptionEnvSlot := self nilObject ].
	^exceptionEnvSlot !

----- Method: PrimitiveProcessor>>isTerminationContextFor (in category 'control primitives') -----
isTerminationContextFor
	| currentCtx startCtx block blockMarkEnv methodMarkEnv matchesP | self halt.
	currentCtx := MTRemoteStackFrame
		gdb: gdb
		pointer: ((self getVRegister: #R) bitAnd: 2r11 bitInvert32).
	startCtx := MTRemoteStackFrame gdb: gdb pointer: ((self getVRegister: #A) bitAnd: 2r11 bitInvert32).
	block := startCtx blockClosure.
	blockMarkEnv := block env markEnv.
	methodMarkEnv := currentCtx env markEnv.
	matchesP := (currentCtx method pointer = block compiledBlock homeMethod pointer) and:
		[ blockMarkEnv pointer = methodMarkEnv pointer ].
	self return: (matchesP 
		ifTrue: [ self trueObject ]
		ifFalse: [ self falseObject ])!

----- Method: PrimitiveProcessor>>isValueMarked (in category 'control primitives') -----
isValueMarked
	| frame matchesP |
	frame := MTRemoteStackFrame gdb: gdb pointer: (self receiverOop  bitAnd: 2r11 bitInvert32).
	matchesP := frame method pointer = (gdb exe externalReferences at: #MT_valueMarked).
	self return: (matchesP 
		ifTrue: [ self trueObject ]
		ifFalse: [ self falseObject ])!

----- Method: PrimitiveProcessor>>markFail (in category 'engine') -----
markFail
	| cr |
self halt.
	cr := regs at: 'cr'.
	cr := cr bitOr: 2r0010 << (4 * 2). "EQ bit in CR5"
	regs at: 'cr' put: cr!

----- Method: PrimitiveProcessor>>markSuccess (in category 'engine') -----
markSuccess
	| cr |
	cr := regs at: 'cr'.
	cr := cr bitAnd: (2r0010 << (4 * 2)) bitInvert32. "EQ bit in CR5"
	regs at: 'cr' put: cr!

----- Method: PrimitiveProcessor>>nilObject (in category 'accessing') -----
nilObject
	^gdb exe externalReferences at: #MT_nil!

----- Method: PrimitiveProcessor>>primAddSI (in category 'integer primitives') -----
primAddSI
	| r a sum |
	r := self getVRegister: #R.
	(r bitAnd: 2r11) = 2r0001 ifFalse:[ ^self markFail ].
	r := gdb exe architecture smallIntegerToInteger: r.
	a := self getVRegister: #A.
	(a bitAnd: 2r11) = 2r0001 ifFalse:[ ^self markFail ].
	a := gdb exe architecture smallIntegerToInteger: a.
	sum := r + a.
	sum := gdb exe architecture integerToSmallInteger: sum.
	self return: sum!

----- Method: PrimitiveProcessor>>primAlignedPointerOopAt (in category 'object access primitives') -----
primAlignedPointerOopAt
	| r idx oop |
	idx := (self getVRegister: #A) >> 4.
	r := (self getVRegister: #R) bitAnd: 2r0011 bitInvert32.
	oop := gdb read32At: r + (idx*4).
	self return: oop!

----- Method: PrimitiveProcessor>>primAlignedPointerOopAtPut (in category 'object access primitives') -----
primAlignedPointerOopAtPut
	| r idx arg2  |
	idx := (self getVRegister: #A) >> 4.
	r := (self getVRegister: #R) bitAnd: 2r00011 bitInvert32.
	arg2 := gdb currentFrame arg: 2.
	gdb writeInt32: arg2 toAddr: r + (idx*4).
	self markSuccess !

----- Method: PrimitiveProcessor>>primBasicAt (in category 'object access primitives') -----
primBasicAt
	| l |
	"NB - guards"
	l := MTRemoteLiveObject gdb: gdb liveObject: (self getVRegister: #R).
	self return: (l mtBasicAt: (self getVRegister: #A) >> 4)!

----- Method: PrimitiveProcessor>>primBasicAtPut (in category 'object access primitives') -----
primBasicAtPut
	| l idx arg2 |
	"NB - guards"
	l := MTRemoteLiveObject gdb: gdb liveObject: (self getVRegister: #R).
	idx := (self getVRegister: #A) >> 4.
	arg2 := self currentFrame arg: 2.
	l mtBasicAt: idx put: arg2.
	self markSuccess !

----- Method: PrimitiveProcessor>>primBasicSize (in category 'primitives') -----
primBasicSize
	| answer |
" NB: insert prim failure guards here, such as SmallInteger "
	answer := (MTRemoteLiveObject gdb: gdb liveObject: self receiverOop) numIndexed.
	answer := (answer bitShift: 4) bitOr: 2r0001. "SI"
	self return: answer!

----- Method: PrimitiveProcessor>>primCharacterBasicAt (in category 'object access primitives') -----
primCharacterBasicAt
	| index char |
	index := self getVRegister: #A.
	"Guard that the arg is an SI:"
	(index bitAnd: 2r00011) = 1 ifFalse: [ ^self markFail ].
	index := index >> 4.
	
	char := gdb readByteAt: (self getVRegister: #R) + 8 + index - 1.
	
	"CHAR_TAG=2"
	char := char << 4 bitOr: 2.
	
	self return: char!

----- Method: PrimitiveProcessor>>primCharacterBasicAtPut (in category 'object access primitives') -----
primCharacterBasicAtPut
	| index char charOop |

	index := self getVRegister: #A.
	"Guard that the index arg is an SI"
	(index bitAnd: 2r00011) = 1 ifFalse: [ ^self markFail ].
	index := index >> 4.
	
	charOop := self currentFrame arg: 2.
	"Guard that the char arg is a char"
	(charOop bitAnd: 2r00011) = 2 ifFalse: [ ^self markFail ].
	char := charOop >> 4.
	
	gdb byteAt: (self getVRegister: #R) + 8 + index - 1 put: char.
	
	self return: charOop!

----- Method: PrimitiveProcessor>>primCharacterFromCodePoint (in category 'object access primitives') -----
primCharacterFromCodePoint
	| arg |
	arg := self getVRegister: #A.
	"Remove the SI tag and attach a CHAR tag"
	arg := (arg bitAnd: 16rFFFFFFF0) bitOr: 2r00010.
	self return: arg!

----- Method: PrimitiveProcessor>>primCharacterValue (in category 'object access primitives') -----
primCharacterValue
	| arg |
	arg := self getVRegister: #A.
	"Remove the CHAR tag and attach a SI tag"
	arg := (arg bitAnd: 16rFFFFFFF0) bitOr: 2r00001.
	self return: arg!

----- Method: PrimitiveProcessor>>primClass (in category 'primitives') -----
primClass
	| rcv md |
	rcv := MTRemoteObject gdb: gdb pointer:  (self getVRegister: #R).
	md := MTRemoteMethodDictionary gdb: gdb pointer: rcv md.
	[ md pointer = self nilObject ] whileFalse: [ 
		| clazz |
		clazz := md definingClass.
		clazz isRemoteNil ifFalse: [ ^self return: clazz pointer ].
		md := md superMd ].
	^self return: self nilObject !

----- Method: PrimitiveProcessor>>primClassName (in category 'primitives') -----
primClassName
	| rcv |
	"The argument is a class."
	rcv := MTRemoteClass gdb: gdb pointer:  (self getVRegister: #R).
	self return: rcv name!

----- Method: PrimitiveProcessor>>primCurrentContext (in category 'control primitives') -----
primCurrentContext
	| context  |
	context := self currentFrame senderFrame.
	self return: (context pointer bitOr: 3)!

----- Method: PrimitiveProcessor>>primExceptionEnvironment (in category 'control primitives') -----
primExceptionEnvironment
	^self return: self getExceptionEnvSlot !

----- Method: PrimitiveProcessor>>primFail (in category 'feature primitives') -----
primFail
	self markFail!

----- Method: PrimitiveProcessor>>primHash (in category 'object access primitives') -----
primHash
	self return: 2r0001. "SI 0"
!

----- Method: PrimitiveProcessor>>primIntDivSI (in category 'integer primitives') -----
primIntDivSI
	| r a d |
	r := self getVRegister: #R.
	(r bitAnd: 2r111) = 2r0001 ifFalse:[ ^self markFail ].
	r := gdb exe architecture smallIntegerToInteger: r.
	a := self getVRegister: #A.
	(a bitAnd: 2r111) = 2r0001 ifFalse:[ ^self markFail ].
	a := gdb exe architecture smallIntegerToInteger: a.
	d := r // a.
	d := gdb exe architecture integerToSmallInteger: d.
	self return: d!

----- Method: PrimitiveProcessor>>primIntRemSI (in category 'integer primitives') -----
primIntRemSI
	| r a rem |
	r := self getVRegister: #R.
	(r bitAnd: 2r11) = 2r0001 ifFalse:[ ^self markFail ].
	r := gdb exe architecture smallIntegerToInteger: r.
	a := self getVRegister: #A.
	(a bitAnd: 2r11) = 2r0001 ifFalse:[ ^self markFail ].
	a := gdb exe architecture smallIntegerToInteger: a.
	rem  := r \\ a.
	rem := gdb exe architecture integerToSmallInteger: rem.
	self return: rem!

----- Method: PrimitiveProcessor>>primIsBottomOfStack (in category 'control primitives') -----
primIsBottomOfStack
	| bottomP frame |
	frame := MTRemoteStackFrame gdb: gdb pointer: ((self getVRegister: #R) bitAnd: 2r11 bitInvert32).
	bottomP := frame isBottomFrame.
	self return: (bottomP 
		ifTrue: [ self trueObject ]
		ifFalse: [ self falseObject ])!

----- Method: PrimitiveProcessor>>primMultiplySI (in category 'integer primitives') -----
primMultiplySI
	| r a p |
	r := self getVRegister: #R.
	(r bitAnd: 2r111) = 2r0001 ifFalse:[ ^self markFail ].
	r := gdb exe architecture smallIntegerToInteger: r.
	a := self getVRegister: #A.
	(a bitAnd: 2r111) = 2r0001 ifFalse:[ ^self markFail ].
	a := gdb exe architecture smallIntegerToInteger: a.
	p := r * a.
	p := gdb exe architecture integerToSmallInteger: p.
	self return: p!

----- Method: PrimitiveProcessor>>primNewIndexedObject (in category 'primitives') -----
primNewIndexedObject
	| clazz size |
self halt.
	size := (self getVRegister: #A) >> 4.
size >= (65536*4) ifTrue:[self halt].
	clazz := MTRemoteClass gdb: gdb pointer: self receiverOop.
	clazz structure = 5
		ifTrue: [ self allocString: size md: clazz instanceMd ]
		ifFalse: [ self allocIndexed: size md: clazz instanceMd ]!

----- Method: PrimitiveProcessor>>primNewObject (in category 'primitives') -----
primNewObject
	| clazz  |
	clazz := MTRemoteClass gdb: gdb pointer: self receiverOop.
	self return: (self
		allocOop: clazz instVarCount
		md: clazz instanceMd)!

----- Method: PrimitiveProcessor>>primPerform (in category 'control primitives') -----
primPerform
	"Perform a 0-arg send.
	Receiver in R, selector in A."
	| a selector fp frame jmpTarget |

	a := self getVRegister: #A.
	fp := self getVRegister: #FP.
	frame := MTRemoteStackFrame gdb: gdb pointer: fp.
	self setVRegister: #SP to: fp-4.
	self setVRegister: #FP to: frame senderFrame pointer.
	
	selector := MTRemoteSymbol gdb: gdb pointer: a.
	jmpTarget := (HostAssistedLookup regBase: self ram: gdb) messageSendSelector: selector symbol.
	"jmpTarget := gdb messageSendSelector: selector symbol."  "correct address to jump to in CTR!!"
	"but the contract with the primitive invocation code is that
	the address is in scratch1."
	self setVRegister: #Scratch1 to: jmpTarget 
	
	!

----- Method: PrimitiveProcessor>>primPreviousContext (in category 'control primitives') -----
primPreviousContext
	| context prevAddr |
	context := self receiverOop.
	context := context bitAnd: 3 bitInvert32.
	prevAddr := gdb read32At: context.
	self return: (prevAddr bitOr: 3)!

----- Method: PrimitiveProcessor>>primPrintString (in category 'feature primitives') -----
primPrintString
	| r tag |
self halt.
	r := self receiverOop.
	tag := r bitAnd: 2r00011.
	tag = 0 ifTrue: [ 
		"For pointer oops, assume it's a String"
		| rs |
		rs := MTRemoteString gdb: gdb pointer: r.
		Transcript show: rs string.
		^self markSuccess 		
	].
	tag = 1 ifTrue: [
		Transcript show: (r>>4) printString.
		^self markSuccess 		
	].
	self halt!

----- Method: PrimitiveProcessor>>primReturnValueFromContext (in category 'control primitives') -----
primReturnValueFromContext
	| ctx |
	self setVRegister: #R to: (self getVRegister: #A).
	ctx := gdb read32At: (self getVRegister: #FP) - 8.
	ctx := ctx bitAnd: 2r11 bitInvert32. "strip off context immediate tag"
	self setVRegister: #FP to: ctx.
	self setVRegister: #SP to: ctx + 4.
	self markSuccess !

----- Method: PrimitiveProcessor>>primSIGreaterThan (in category 'integer primitives') -----
primSIGreaterThan
	self return: ((self getVRegister: #R) > (self getVRegister: #A)
		ifTrue: [ self trueObject ]
		ifFalse: [ self falseObject ])!

----- Method: PrimitiveProcessor>>primSIGreaterThanEqual (in category 'integer primitives') -----
primSIGreaterThanEqual
	self return: ((gdb getVRegister: #R) >= (gdb getVRegister: #A)
		ifTrue: [ self trueObject ]
		ifFalse: [ self falseObject ])!

----- Method: PrimitiveProcessor>>primSILessThan (in category 'integer primitives') -----
primSILessThan
	self return: ((self getVRegister: #R) < (self getVRegister: #A)
		ifTrue: [ self trueObject ]
		ifFalse: [ self falseObject ])!

----- Method: PrimitiveProcessor>>primSILessThanEqual (in category 'integer primitives') -----
primSILessThanEqual
	self return: ((self getVRegister: #R) <= (self getVRegister: #A)
		ifTrue: [ self trueObject ]
		ifFalse: [ self falseObject ])!

----- Method: PrimitiveProcessor>>primSay (in category 'feature primitives') -----
primSay
	" Show the receiver SI on the host transcript. "
	| something tag |
self halt.
	something := self getVRegister: #R.
	tag := something bitAnd: 2r00011.
	tag = 0 ifTrue: [ something := 'oop:', something asString, ' md:', (MTRemoteObject gdb: gdb pointer: something) md printString ].
	tag = 1 ifTrue: [ something := gdb exe architecture smallIntegerToInteger: something ].
	
	Transcript show: ('Modtalk says: ', something asString); cr.
	self markSuccess.


	!

----- Method: PrimitiveProcessor>>primSay2 (in category 'feature primitives') -----
primSay2
	" On the class side of ProtoObject. "
self halt.
	self markSuccess.


	!

----- Method: PrimitiveProcessor>>primSaySomething (in category 'feature primitives') -----
primSaySomething
self halt.
	Transcript show: 'It WORKS!!!!!!'; cr!

----- Method: PrimitiveProcessor>>primSaySomethingElse (in category 'feature primitives') -----
primSaySomethingElse
	Transcript show: 'It DOES NOT WORK!!!!!!'; cr!

----- Method: PrimitiveProcessor>>primSetExceptionEnvironment (in category 'control primitives') -----
primSetExceptionEnvironment
	| ee |
	ee := self getVRegister: #A.
	self setExceptionEnvSlot: ee;
		markSuccess !

----- Method: PrimitiveProcessor>>primStringCompare (in category 'feature primitives') -----
primStringCompare
	| r b answer |
	r := self getVRegister: #R.
	r := MTRemoteString gdb: gdb pointer: r.
	r := r string.
	
	b := self getVRegister: #A.
	b := MTRemoteString gdb: gdb pointer: b.
	b := b string.
	
	answer := r = b ifTrue: [2] ifFalse: [ r < b ifTrue: [1] ifFalse: [3] ].
	answer := gdb exe architecture integerToSmallInteger: answer.
	self return: answer!

----- Method: PrimitiveProcessor>>primSubSI (in category 'integer primitives') -----
primSubSI
	| r a diff |
	r := self getVRegister: #R.
	(r bitAnd: 2r111) = 2r0001 ifFalse:[ ^self markFail ].
	r := gdb exe architecture smallIntegerToInteger: r.
	a := self getVRegister: #A.
	(a bitAnd: 2r111) = 2r0001 ifFalse:[ ^self markFail ].
	a := gdb exe architecture smallIntegerToInteger: a.
	diff := r - a.
	diff := gdb exe architecture integerToSmallInteger: diff.
	self return: diff!

----- Method: PrimitiveProcessor>>primValue (in category 'control primitives') -----
primValue
	| addr |
	addr := (MTRemoteBlockClosure gdb: gdb pointer: self receiverOop)
		compiledBlock codeRef address.
	self setVRegister: #Scratch1 to: addr.
	self setVRegister: #X to: self receiverOop.
	self markSuccess !

----- Method: PrimitiveProcessor>>primValueWith2Args (in category 'control primitives') -----
primValueWith2Args
	| arg2 |
	arg2 := self currentFrame arg: 2.
	gdb push: arg2.
	^self primValue!

----- Method: PrimitiveProcessor>>primValueWithArgument (in category 'control primitives') -----
primValueWithArgument
	^self primValue "No need to specifically pass the arg because it is already in #A"!

----- Method: PrimitiveProcessor>>primValueWithArguments (in category 'control primitives') -----
primValueWithArguments
self halt!

----- Method: PrimitiveProcessor>>receiverOop (in category 'engine') -----
receiverOop
	^self getVRegister: #R!

----- Method: PrimitiveProcessor>>return: (in category 'engine') -----
return: oop
	self setVRegister: #R to: oop.
	self markSuccess!

----- Method: PrimitiveProcessor>>setExceptionEnvSlot: (in category 'accessing') -----
setExceptionEnvSlot: oop
	exceptionEnvSlot := oop!

----- Method: PrimitiveProcessor>>trueObject (in category 'accessing') -----
trueObject
	^gdb exe externalReferences at: #MT_true!

BasePrimitiveProcessor subclass: #PrivatePrimitiveProcessor
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Primitives'!

----- Method: PrivatePrimitiveProcessor>>primAllocEnv (in category 'primitives') -----
primAllocEnv
	| stackFrame count ptr env |
	count := gdb currentBytecode count.
	stackFrame := self currentFrame.
	ptr := self allocSlots: count serviceSlots: 2 "header, hash".
	env := MTRemoteIndexedPart gdb: gdb pointer: ptr.
	env header: (TAJObjectWriter
		declareObjectHeaderType: 'IndexedPartType'
		hashFormat: 'NoHash'
		numVars: count
		hash: 0
		meta: 0).
	env basicSlotAt: 1 put: stackFrame env pointer.
	stackFrame env: env.
	self setVRegister: #X to: ptr.
!

----- Method: PrivatePrimitiveProcessor>>primMethodClosure (in category 'primitives') -----
primMethodClosure
	|  stackFrame method blocks ptr md count block blockClosure numArgs blockClosureMDKey |

	count := gdb currentBytecode count.
	stackFrame := self currentFrame.
	method := stackFrame method.
	blocks := method blocks.
	block := MTRemoteCompiledBlock gdb: gdb pointer: (blocks at: count).
	numArgs := block numArgs.
	blockClosureMDKey := (Array
		with: #MTZeroArgumentBlockClosure_md
		with: #MTOneArgumentBlockClosure_md
		with: #MTTwoArgumentBlockClosure_md
	) at: numArgs + 1.
	md := gdb exe externalReferences at: blockClosureMDKey.
	ptr := self allocOop: 4 md: md.
	blockClosure := MTRemoteBlockClosure gdb: gdb pointer: ptr.
	blockClosure 
		literals: method literals;
		compiledBlock: block;
		env:(stackFrame env);
		receiver: (MTRemoteObject gdb: gdb pointer: (self getVRegister: #R)).
	self setVRegister: #R to: ptr.!

Object subclass: #DebugStopped
	instanceVariableNames: 'signal'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: DebugStopped class>>onSignalNum: (in category 'instance creation') -----
onSignalNum: anInteger
	^self basicNew
		signal: (self signalNames at: anInteger)
		yourself!

----- Method: DebugStopped class>>signalNames (in category 'signal numbers') -----
signalNames
	^#(
	SIGHUP
	SIGINT
	SIGQUIT
	SIGILL
	SIGTRAP
	SIGABRT
	SIGBUS
	SIGFPE
	SIGKILL
	SIGUSR1
	SIGSEGV
	SIGUSR2
	SIGPIPE
	SIGALRM
	SIGTERM
	SIGSTKFLT
	SIGCHLD
	SIGCONT
	SIGSTOP
	SIGTSTP
	SIGTTIN
	SIGTTOU
	SIGURG
	SIGXCPU
	SIGXFSZ
	SIGVTALRM
	SIGPROF
	SIGWINCH
	SIGIO
	SIGPWR
	SIGSYS
	SIGRTMIN
	)!

----- Method: DebugStopped>>printOn: (in category 'printing') -----
printOn: aStream
	aStream nextPutAll: 'Got '; nextPutAll: self signal asString!

----- Method: DebugStopped>>signal (in category 'accessing') -----
signal
	^signal!

----- Method: DebugStopped>>signal: (in category 'accessing') -----
signal: aSymbol
	signal := aSymbol!

Object subclass: #Doodle
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: Doodle>>assert:equals: (in category 'as yet unclassified') -----
assert: expected equals: actual
	^self
		assert: expected = actual
!

Doodle subclass: #GDBDoodle
	instanceVariableNames: 'gdb'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

GDBDoodle subclass: #AbsoluteZeroPPC
	instanceVariableNames: 'memory'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

!AbsoluteZeroPPC commentStamp: 'bgs 3/25/2020 06:56' prior: 0!
See thinshell/absolute/power/absolute.s.
This gets built into an exe loaded at absolute address 0.
Execution starts at 0.
gem5 is able to simulate an MMU that can map such address.
!

----- Method: AbsoluteZeroPPC>>connectGdb (in category 'target connection') -----
connectGdb
	super connectGdb.
	memory := RemoteRAM gdb: gdb!

----- Method: AbsoluteZeroPPC>>hostIP (in category 'target connection') -----
hostIP
	^'192.168.75.2'!

----- Method: AbsoluteZeroPPC>>makeAFewSteps (in category 'stepping logic') -----
makeAFewSteps
	self assert: gdb pc equals: 0.
	self assert: memory currentInstructionEncoding equals: 16r7c631a78.
	gdb s.
	self assert: gdb pc equals: 4.
	self assert: memory currentInstructionEncoding equals: 16r3860002a.
	gdb s.
	self assert: gdb pc equals: 8.
!

----- Method: AbsoluteZeroPPC>>pdl (in category 'target connection') -----
pdl
	^FakeProcessorDescriptionPPC new!

----- Method: AbsoluteZeroPPC>>testAbs (in category 'target connection') -----
testAbs
	"
	AbsoluteZeroPPC  new halt ;  testAbs
	"
	self connectGdb; 	makeAFewSteps.
	[ "then, run at full speed until the exit() syscall"
	gdb c
		"At this point we expect gem5 to have exited and said,
		Exit code is 42"
	] on: GdbChildExited do: [ ^self ].
	self error!

----- Method: GDBDoodle>>connectGdb (in category 'target connection') -----
connectGdb
	gdb := self debuggerClass
		host: self hostIP
		port: self tcpPort
		processorDescription: self pdl.
	^gdb!

----- Method: GDBDoodle>>debuggerClass (in category 'target connection') -----
debuggerClass
	^RemoteGDBSession!

----- Method: GDBDoodle>>hostIP (in category 'target connection') -----
hostIP
	self shouldBeImplemented!

----- Method: GDBDoodle>>pdl (in category 'target connection') -----
pdl
	self shouldBeImplemented!

----- Method: GDBDoodle>>tcpPort (in category 'target connection') -----
tcpPort
	^7000!

GDBDoodle subclass: #ThinshellDoodle
	instanceVariableNames: 'memory'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

ThinshellDoodle subclass: #PPCThinshellDoodle
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

PPCThinshellDoodle subclass: #P1025ThinshellDoodle
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: P1025ThinshellDoodle>>hostIP (in category 'target connection') -----
hostIP
	^'192.168.75.199'!

----- Method: P1025ThinshellDoodle>>pdl (in category 'target connection') -----
pdl
	^FakeProcessorDescriptionP1025 new!

----- Method: P1025ThinshellDoodle>>testStepThoughPreamble (in category 'tests') -----
testStepThoughPreamble
	"
	P1025ThinshellDoodle new testStepThoughPreamble
	"
	self connectGdb; stepThroughThinshellPreamble.
	[ gdb kill ] on: GdbChildExited do: [ ^self ].
	"should be unreachable"
	self error!

----- Method: P1025ThinshellDoodle>>testSurgicalJump (in category 'tests') -----
testSurgicalJump
	"
	P1025ThinshellDoodle new testSurgicalJump
	"
	self connectGdb; stepThroughThinshellPreamble.

	gdb setRegister: 'r1' to: 240.
	gdb setRegister: 'pc' to: 16r100000cc.
	[ gdb c ] on: InferiorExited do: [ :ex |
		"We expect gdbserver to say, Child exited with status 15"
		self assert: ex exitCode equals: 15.
		"The RSP protocol spec doesn't say what is allowed here,
		because targets can vary.
		For example, attempting to terminate the gdbserver by sending KILL,
		will not work with the normal GNU gdbserver.
		However, simply closing the connection will suffice."
		^gdb socket close
		].
	
	"shouldn't reach here"
	self error!

PPCThinshellDoodle subclass: #PPCgem5ThinshellDoodle
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

PPCgem5ThinshellDoodle subclass: #HardwareBreakpointDoodle
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: HardwareBreakpointDoodle>>hostIP (in category 'target connection') -----
hostIP
	^'192.168.75.2'!

----- Method: HardwareBreakpointDoodle>>installBrk (in category 'tests') -----
installBrk
	gdb insertHWBreakpointAt: self initialPC + 8!

----- Method: HardwareBreakpointDoodle>>pdl (in category 'target connection') -----
pdl
	^FakeProcessorDescriptionPPC new!

----- Method: HardwareBreakpointDoodle>>testHWBrk (in category 'tests') -----
testHWBrk
	"
	HardwareBreakpointDoodle new halt; testHWBrk.
	"
	self connectGdb;
		installBrk.
	gdb c.
	self assert: gdb pc equals: self initialPC + 8.
	self fillNZone.
	
	"No need to advance, because this is not a trap."
	[ gdb c ] on: GdbChildExited do: [ ^self ].
	self error!

PPCgem5ThinshellDoodle subclass: #PPCIllegalStoreDoodle
	instanceVariableNames: 'isHardware'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: PPCIllegalStoreDoodle class>>onHardware (in category 'instance creation') -----
onHardware
	"Set up the test for the real devboard."
	^self basicNew isHardware: true!

----- Method: PPCIllegalStoreDoodle class>>onSoftware (in category 'instance creation') -----
onSoftware
	"Set up the test for gem5."
	^self basicNew isHardware: false!

----- Method: PPCIllegalStoreDoodle>>hostIP (in category 'target connection') -----
hostIP
	^isHardware
		ifTrue: [ '192.168.75.199' ]
		ifFalse: [ '192.168.75.2' ]!

----- Method: PPCIllegalStoreDoodle>>isHardware: (in category 'as yet unclassified') -----
isHardware: whetherToUseP1025
	isHardware := whetherToUseP1025.
	^self!

----- Method: PPCIllegalStoreDoodle>>pdl (in category 'target connection') -----
pdl
	^isHardware
		ifTrue: [ FakeProcessorDescriptionP1025 new ]
		ifFalse: [ FakeProcessorDescriptionPPC new ]!

----- Method: PPCIllegalStoreDoodle>>stepThroughThinshellPreamble (in category 'as yet unclassified') -----
stepThroughThinshellPreamble

	self assert: memory currentInstructionEncoding equals: 16r3920ffff. "li r9,-1"
self halt.
	gdb c.
	self assert: memory currentInstructionEncoding equals: 16r90090000. "stw r0,0(r9)"
self halt.
	gdb s. "Simulation dies"
!

----- Method: PPCIllegalStoreDoodle>>testSTW (in category 'as yet unclassified') -----
testSTW
	"
	PPCIllegalStoreDoodle onSoftware testSTW.
	
	PPCIllegalStoreDoodle onHardware testSTW.
	"
	self connectGdb;
		stepThroughThinshellPreamble!

----- Method: PPCgem5ThinshellDoodle>>hostIP (in category 'target connection') -----
hostIP
	^'192.168.75.2'!

----- Method: PPCgem5ThinshellDoodle>>pdl (in category 'target connection') -----
pdl
	^FakeProcessorDescriptionPPC new!

----- Method: PPCgem5ThinshellDoodle>>testManualNZone (in category 'tests') -----
testManualNZone
	"
	PPCgem5ThinshellDoodle new halt;  testManualNZone
	"
	self connectGdb;
		stepThroughThinshellPreamble;
		fillNZone.
	[ "then, run at full speed until the exit() syscall"
	gdb c
		"At this point we expect gem5 to have exited and said,
		Exit code is 42"
	] on: GdbChildExited do: [ ^self ].
	self error
!

----- Method: PPCgem5ThinshellDoodle>>testManualNZone2 (in category 'tests') -----
testManualNZone2
	"
	PPCgem5ThinshellDoodle new testManualNZone2
	"
	self connectGdb;
		stepThroughThinshellPreamble;
		fillNZone2.
	[ "then, run at full speed until the exit() syscall"
	gdb c
		"At this point we expect gem5 to have exited and said,
		Exit code is 42"
	] on: GdbChildExited do: [ ^self ].
	self error
!

PPCgem5ThinshellDoodle subclass: #RemoteMemoryDoodle
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: RemoteMemoryDoodle>>hostIP (in category 'target connection') -----
hostIP
	^'192.168.75.2'!

----- Method: RemoteMemoryDoodle>>pdl (in category 'target connection') -----
pdl
	^FakeProcessorDescriptionPPC new!

----- Method: RemoteMemoryDoodle>>testLowLevelWrite (in category 'tests-reading') -----
testLowLevelWrite
	"
	RemoteMemoryDoodle new testLowLevelWrite
	"
	| goodAddress |
	self connectGdb.
	goodAddress := self initialPC.
	memory writeBytes: #[1 2 3 4] toAddr: goodAddress.
	
	self assert: (memory unsignedLongAtAddr: goodAddress bigEndian: true) equals: 16r01020304.
	self assert: (memory unsignedLongAt: goodAddress + 1 bigEndian: true) equals: 16r01020304.
	self assert: (memory read: 4 bytesAtAddr: goodAddress) equals: #[1 2 3 4].
	self assert: (memory unsignedByteAtAddr: 16r10000080) equals: 1.
	self assert: (memory unsignedByteAtAddr: 16r10000081) equals: 2.
	
	[ gdb kill ] on: GdbChildExited do: [ ^self ]
	!

----- Method: RemoteMemoryDoodle>>testReadFirstInstruction (in category 'tests-reading') -----
testReadFirstInstruction
	"
	RemoteMemoryDoodle new testReadFirstInstruction
	"
	| entry |
	self connectGdb.
	entry := self initialPC.
	
	self assert: (memory unsignedLongAtAddr: entry bigEndian: true) equals: 16r7c000378.
	self assert: (memory unsignedLongAtAddr: entry bigEndian: false) equals: 16r7803007c.
	self assert: (memory read: 4 bytesAtAddr: entry) equals: #[124 0 3 120].
	self assert: (memory unsignedByteAtAddr: 16r10000080) equals: 124.
	
	[ gdb kill ] on: GdbChildExited do: [ ^self ]
!

RemoteMemoryDoodle subclass: #ShmemDoodle
	instanceVariableNames: 'plainRAM'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: ShmemDoodle>>connectGdb (in category 'target connection') -----
connectGdb
	super connectGdb.
	memory shmemSize: 536870912. "size of TAM's thinshell process image"
	plainRAM := RemoteRAM gdb: gdb.
	^gdb!

----- Method: ShmemDoodle>>doodles (in category 'tests-agreement') -----
doodles

ShmemDoodle new testLowLevelWrite.

ShmemDoodle new testReadFirstInstruction.

ShmemDoodle new testManualNZone.

ShmemDoodle new testManualNZone2.

!

----- Method: ShmemDoodle>>remoteMemoryClass (in category 'target connection') -----
remoteMemoryClass
	^Gem5SharedRAM!

----- Method: ShmemDoodle>>testSharedToNonShared (in category 'tests-agreement') -----
testSharedToNonShared!

----- Method: ThinshellDoodle>>connectGdb (in category 'target connection') -----
connectGdb
	super connectGdb.
	self createRAM!

----- Method: ThinshellDoodle>>createRAM (in category 'target connection') -----
createRAM
	memory := self remoteMemoryClass gdb: gdb!

----- Method: ThinshellDoodle>>fillNZone (in category 'stepping logic') -----
fillNZone
	"Fill the nZone with (programmer-assembled) machine code
	that will return the SmallInteger 42.
	The Thinshell will convert the TAM SmallInteger in #R
	(which is where the Program Initializer is expected to put it)
	into a machine integer suitable for passing to the exit() syscall."

	memory writeInt32: 16r382002a0 toAddr: self nZone+0. "li r1,672"
	memory writeInt32: 16r4e800020 toAddr: self nZone+4. "blr"
!

----- Method: ThinshellDoodle>>fillNZone2 (in category 'stepping logic') -----
fillNZone2
	"Same as fillNZone, but in one shot."

	memory writeInt32s: #(16r382002a0 16r4e800020) toAddr: self nZone+0!

----- Method: ThinshellDoodle>>initialPC (in category 'facts about thinshell') -----
initialPC
	^16r10000080
!

----- Method: ThinshellDoodle>>nZone (in category 'facts about thinshell') -----
nZone
	^16r100100E0!

----- Method: ThinshellDoodle>>remoteMemoryClass (in category 'target connection') -----
remoteMemoryClass
	^RemoteRAM!

----- Method: ThinshellDoodle>>stepThroughThinshellPreamble (in category 'stepping logic') -----
stepThroughThinshellPreamble
	| initialPC nZone |
	initialPC := gdb pc.
	self assert: initialPC equals: 16r10000080.
	self assert: memory currentInstructionEncoding equals: 16r7C000378. "mr r0,r0"
	gdb s.
	self assert: gdb pc equals: initialPC + 4.
	self assert: memory currentInstructionEncoding equals: 16r3E001001. "lis r16,4097"
	gdb s.
	self assert: gdb pc equals: initialPC + 8.
	gdb s; s; s; s; s. "last non-nop"
	nZone := gdb getRegisters at: 'r16'.
	self assert: nZone equals: self nZone.
	gdb s; s; s; s; s; s. "bunch of nops (there are more!!)"
	self assert: gdb pc equals: 16r100000B4!

GDBDoodle subclass: #X86JumpDoodle
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: X86JumpDoodle>>hostIP (in category 'target connection') -----
hostIP
	^'192.168.75.2'!

----- Method: X86JumpDoodle>>pdl (in category 'target connection') -----
pdl
	^FakeProcessorDescriptionX86 new!

----- Method: X86JumpDoodle>>tcpPort (in category 'target connection') -----
tcpPort
	^7000!

----- Method: X86JumpDoodle>>testAtomicSimpleCPU (in category 'tests') -----
testAtomicSimpleCPU
	"
	X86JumpDoodle new testAtomicSimpleCPU
	"
	self halt; connectGdb.
	gdb pc: 17.
	gdb s.
	gdb pc.
	gdb getRegisters at: 'eax'.
	gdb getRegisters at: 'ebp'.!

Object subclass: #FakeProcessorDescription
	instanceVariableNames: 'regsInGPacket'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: FakeProcessorDescription>>endian (in category 'accessing') -----
endian 
	^self class endian!

----- Method: FakeProcessorDescription>>initRegsInGPacket (in category 'accessing') -----
initRegsInGPacket
	"Do not try to use the real GdbXmlParser.
	On some Smalltalks under development, there is no XML."
	| regNum |
	regNum := 0.
	regsInGPacket := self class fakeFeatures collect: [ :reg |
		| rt |
		rt := RSPOneRegisterTransfer new
			regName: (reg first);
			width: (reg second);
			isLittleEndian: (self endian = #little);
			regNum: regNum;
			yourself.
		regNum := regNum + 1.
		
		rt ]!

----- Method: FakeProcessorDescription>>regsInGPacket (in category 'accessing') -----
regsInGPacket
	regsInGPacket isNil ifTrue: [ self initRegsInGPacket ].
	^regsInGPacket !

FakeProcessorDescription subclass: #FakeProcessorDescriptionPPC
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

FakeProcessorDescriptionPPC subclass: #FakeProcessorDescriptionP1025
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

!FakeProcessorDescriptionP1025 commentStamp: 'BorisShingarov 3/13/2020 01:27' prior: 0!
This will go away when we implement xi:include!

----- Method: FakeProcessorDescriptionP1025 class>>fakeFeatures (in category 'as yet unclassified') -----
fakeFeatures
	^#(
#('r0' 32)  #('r1' 32)  #('r2' 32)  #('r3' 32)  #('r4' 32)  #('r5' 32)  #('r6' 32)  #('r7' 32)
#('r8' 32)  #('r9' 32)  #('r10' 32) #('r11' 32) #('r12' 32) #('r13' 32) #('r14' 32) #('r15' 32)
#('r16' 32) #('r17' 32) #('r18' 32) #('r19' 32) #('r20' 32) #('r21' 32) #('r22' 32) #('r23' 32)
#('r24' 32) #('r25' 32) #('r26' 32) #('r27' 32) #('r28' 32) #('r29' 32) #('r30' 32) #('r31' 32)

#('ev0h' 32)  #('ev1h' 32)  #('ev2h' 32)  #('ev3h' 32)  #('ev4h' 32)  #('ev5h' 32)  #('ev6h' 32)  #('ev7h' 32)
#('ev8h' 32)  #('ev9h' 32)  #('ev10h' 32) #('ev11h' 32) #('ev12h' 32) #('ev13h' 32) #('ev14h' 32) #('ev15h' 32)
#('ev16h' 32) #('ev17h' 32) #('ev18h' 32) #('ev19h' 32) #('ev20h' 32) #('ev21h' 32) #('ev22h' 32) #('ev23h' 32)
#('ev24h' 32) #('ev25h' 32) #('ev26h' 32) #('ev27h' 32) #('ev28h' 32) #('ev29h' 32) #('ev30h' 32) #('ev31h' 32)

#('pc' 32)
#('msr' 32)
#('cr' 32)
#('lr' 32)
#('ctr' 32)
#('xer' 32)

#('orig_r3' 32)
#('trap' 32)
#('acc' 64)
#('spefscr' 32)
)
!

----- Method: FakeProcessorDescriptionPPC class>>endian (in category 'as yet unclassified') -----
endian 
	^#big!

----- Method: FakeProcessorDescriptionPPC class>>fakeFeatures (in category 'as yet unclassified') -----
fakeFeatures
	^#(
#('r0' 32)  #('r1' 32)  #('r2' 32)  #('r3' 32)  #('r4' 32)  #('r5' 32)  #('r6' 32)  #('r7' 32)
#('r8' 32)  #('r9' 32)  #('r10' 32) #('r11' 32) #('r12' 32) #('r13' 32) #('r14' 32) #('r15' 32)
#('r16' 32) #('r17' 32) #('r18' 32) #('r19' 32) #('r20' 32) #('r21' 32) #('r22' 32) #('r23' 32)
#('r24' 32) #('r25' 32) #('r26' 32) #('r27' 32) #('r28' 32) #('r29' 32) #('r30' 32) #('r31' 32)

#('f0' 64)  #('f1' 64)  #('f2' 64)  #('f3' 64)  #('f4' 64)  #('f5' 64)  #('f6' 64)  #('f7' 64)
#('f8' 64)  #('f9' 64)  #('f10' 64) #('f11' 64) #('f12' 64) #('f13' 64) #('f14' 64) #('f15' 64)
#('f16' 64) #('f17' 64) #('f18' 64) #('f19' 64) #('f20' 64) #('f21' 64) #('f22' 64) #('f23' 64)
#('f24' 64) #('f25' 64) #('f26' 64) #('f27' 64) #('f28' 64) #('f29' 64) #('f30' 64) #('f31' 64)

#('pc' 32)
#('msr' 32)
#('cr' 32)
#('lr' 32)
#('ctr' 32)
#('xer' 32)
)
!

----- Method: FakeProcessorDescriptionPPC>>architectureName (in category 'as yet unclassified') -----
architectureName
	^'powerpc'!

FakeProcessorDescription subclass: #FakeProcessorDescriptionX86
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-Doodles'!

----- Method: FakeProcessorDescriptionX86 class>>endian (in category 'as yet unclassified') -----
endian 
	^#little!

----- Method: FakeProcessorDescriptionX86 class>>fakeFeatures (in category 'as yet unclassified') -----
fakeFeatures
	^#(
#('eax' 32)  #('ecx' 32)  #('edx' 32)  #('ebx' 32)  #('esp' 32)  #('ebp' 32)  #('esi' 32)  #('edi' 32)
#('eip' 32)  #('eflags' 32)  #('cs' 32) #('ss' 32) #('ds' 32) #('es' 32) #('fs' 32) #('gs' 32)
)
"
#('st0' 80) #('st1' 80) #('st2' 80) #('st3' 80) #('st4' 80) #('st5' 80) #('st6' 80) #('st7' 80)

#('fctrl' 32)
#('fstat' 32)
#('ftag' 32)
#('fiseg' 32)
#('fioff' 32)
#('foseg' 32)
#('fooff' 32)
#('fop' 32)
)
"!

----- Method: FakeProcessorDescriptionX86>>architectureName (in category 'as yet unclassified') -----
architectureName
	^'x86'!

----- Method: FakeProcessorDescriptionX86>>pcRegisterName (in category 'as yet unclassified') -----
pcRegisterName
	^'eip'!

Object subclass: #GdbXmlParser
	instanceVariableNames: 'isLittleEndian'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: GdbXmlParser class>>endian: (in category 'API') -----
endian: aSymbol
	| le |
	aSymbol == #big ifTrue: [ le := false ]
		ifFalse: [ aSymbol == #little ifTrue: [ le := true ] ifFalse: [ self error: 'Endian must be big or little' ]].
	^self basicNew 
		isLittleEndian: le!

----- Method: GdbXmlParser class>>new (in category 'API') -----
new
	self error: 'Please use #endian:'!

----- Method: GdbXmlParser class>>parseFile:in:assumeEndian: (in category 'API') -----
parseFile: fileName in: path assumeEndian: aSymbol
	| f s |
	f := FileStream fileNamed:
		path, '/', fileName.
	s := f contentsOfEntireFile.
	f close.
	^ (self endian: aSymbol) parseString: s!

----- Method: GdbXmlParser>>isLittleEndian: (in category 'private') -----
isLittleEndian: aBoolean
	isLittleEndian := aBoolean.
	^self!

----- Method: GdbXmlParser>>parseString: (in category 'API') -----
parseString: s
	| parser |
	parser := XMLDOMParser on: s.
	^self processXML: parser parseDocument root.!

----- Method: GdbXmlParser>>processXML: (in category 'private') -----
processXML: root
	| oneRegTransfers regNum feature regs |
	feature := root nodesDetect: [ :aChild | aChild isElementNamed: 'feature' ].
	regs := feature nodesSelect:  [ :aChild | aChild isElementNamed: 'reg' ].
	regNum := 0.
	oneRegTransfers := regs collect: [ :regNode |
		| rt |
		rt := RSPOneRegisterTransfer new
			regName: (regNode attributeAt: 'name');
			width: ((regNode attributeAt: 'bitsize') asInteger);
			isLittleEndian: isLittleEndian;
			regNum: regNum;
			yourself.
		regNum := regNum + 1.
		rt ].
	
	oneRegTransfers := oneRegTransfers asOrderedCollection sorted: [ :rA :rB | rA regNum <= rB regNum ].
	
	"Verify that we got all registers from 0 to the total number, without holes."
	regNum := 0.
	oneRegTransfers do: [ :rt |
		rt regNum == regNum ifFalse: [ self error ].
		regNum := regNum + 1 ].
	^oneRegTransfers!

Object subclass: #HostAssistedLookup
	instanceVariableNames: 'regBase ram'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: HostAssistedLookup class>>regBase:ram: (in category 'instance creation') -----
regBase: regBase ram: ram
	^self new 
		regBase: regBase;
		ram: ram;
		yourself!

----- Method: HostAssistedLookup>>activateCM: (in category 'API') -----
activateCM: cm
	| jmpTarget |
	jmpTarget := cm codeRef address.
	regBase
		setRegister: 'ctr' to: jmpTarget; "we should do something about it"
		setVRegister: #X to: cm pointer.
	^jmpTarget!

----- Method: HostAssistedLookup>>messageSendSelector: (in category 'API') -----
messageSendSelector: selectorSymbol
	| receiver  mdRef md  cm |
	receiver := regBase getVRegister: #R.
	mdRef := ram mdFor: receiver.
	md := MTRemoteMethodDictionary gdb: ram pointer: mdRef.
	cm := md lookupSelector: selectorSymbol.
	^self activateCM: cm!

----- Method: HostAssistedLookup>>ram (in category 'accessing') -----
ram
	^ ram!

----- Method: HostAssistedLookup>>ram: (in category 'accessing') -----
ram: anObject
	ram := anObject!

----- Method: HostAssistedLookup>>regBase (in category 'accessing') -----
regBase
	^ regBase!

----- Method: HostAssistedLookup>>regBase: (in category 'accessing') -----
regBase: anObject
	regBase := anObject!

Object subclass: #MTXMemory
	instanceVariableNames: 'ram layout currentHeapAllocPtr setup'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-TAJ'!

----- Method: MTXMemory class>>jumpTableSize (in category 'as yet unclassified') -----
jumpTableSize
	" In entries as opposed to bytes "
	^1024
!

----- Method: MTXMemory>>alloc: (in category 'as yet unclassified') -----
alloc: nBytes
	^self baseAlloc: nBytes + 4 "for the hash on 32-bit machines"!

----- Method: MTXMemory>>architecture (in category 'as yet unclassified') -----
architecture
	^self targetSetup architecture!

----- Method: MTXMemory>>baseAlloc: (in category 'as yet unclassified') -----
baseAlloc: nBytes
	| evenBytes answer |
	answer := currentHeapAllocPtr.
	nBytes \\ 16 = 0
		ifTrue: [ evenBytes := nBytes ]
		ifFalse: 	[ evenBytes := (nBytes bitOr: 16r0F) + 1 ].
	currentHeapAllocPtr := currentHeapAllocPtr + evenBytes.
	^answer!

----- Method: MTXMemory>>currentGrade (in category 'as yet unclassified') -----
currentGrade!

----- Method: MTXMemory>>initialize (in category 'as yet unclassified') -----
initialize
	self initializeGDB.
	^self!

----- Method: MTXMemory>>initializeGDB (in category 'as yet unclassified') -----
initializeGDB
	setup := TAJTargetSetup current.
	ram := setup gdbClass  
		host: setup host
		port: setup port
		processorDescription: setup architecture isa.
	layout := ThinshellAddressLayout gdb: ram.
	currentHeapAllocPtr := layout heap + (self class jumpTableSize * 4).
	^self!

----- Method: MTXMemory>>jumpTable (in category 'as yet unclassified') -----
jumpTable
	^layout heap
!

----- Method: MTXMemory>>layout (in category 'as yet unclassified') -----
layout
	^layout!

----- Method: MTXMemory>>newChunk (in category 'as yet unclassified') -----
newChunk!

----- Method: MTXMemory>>ram (in category 'as yet unclassified') -----
ram
	^ram!

----- Method: MTXMemory>>startAllocatingOnTarget (in category 'as yet unclassified') -----
startAllocatingOnTarget
	ram setVRegister: #HEAP to: currentHeapAllocPtr!

----- Method: MTXMemory>>targetSetup (in category 'as yet unclassified') -----
targetSetup
	^setup!

Object subclass: #MagicSurgeon
	instanceVariableNames: 'gdb'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

!MagicSurgeon commentStamp: 'BorisShingarov 3/7/2020 20:00' prior: 0!
Execution of the inner (target) Smalltalk VM stopped at a surgery point.!

MagicSurgeon subclass: #MTHostCallback
	instanceVariableNames: 'primitiveProcessor privatePrimitiveProcessor'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-TAJ'!

----- Method: MTHostCallback class>>gdb: (in category 'instance creation') -----
gdb: gdb
	^self new 
		gdb: gdb;
		yourself!

----- Method: MTHostCallback>>call (in category 'surgery') -----
call
	| methodDef cmRef regs |
	methodDef := gdb currentBytecode method value.
	regs := gdb getRegisters.
	regs at: 'ctr' put: (gdb exe externalReferences at: methodDef codeRuntimeName).
	cmRef := gdb exe externalReferences at: methodDef globalName.
	regs at: (TAJWriter registerMap at: #X) put: cmRef.
	gdb setRegisters: regs.
	gdb s!

----- Method: MTHostCallback>>getRegisters (in category 'surgery') -----
getRegisters
self halt!

----- Method: MTHostCallback>>handleDNU (in category 'surgery') -----
handleDNU
	| regs receiver selector className |
	regs := gdb getRegisters.
	receiver := regs at: 'r1'.
	receiver := MTRemoteObject gdb: gdb pointer: receiver.
	className := receiver mdObject definingClass name symbol.
	selector := regs at: 'r7'.
	selector := MTRemoteSymbol gdb: gdb pointer: selector.
	selector := selector symbol.
	self halt!

----- Method: MTHostCallback>>inspectionPoint (in category 'surgery') -----
inspectionPoint
self halt.
"	selectorOop := self getRegisters at: 'r7'.
	primSaySelector := exe externalReferences at: #Symbol_primSay.
	selectorOop = primSaySelector ifTrue: [ 
		siOOP := self getVRegister: #R.
		self pharoObjectForOop: siOOP.
		self halt.
		siMD := self mdFor: siOOP.
		superMD := (MTRemoteMethodDictionary gdb: self pointer: siMD) superMd.
		superSuperMD := superMD superMd.
		 ]."
	"anotherWay := self currentBytecode selector value."
	"gdb s"!

----- Method: MTHostCallback>>messageSend (in category 'surgery') -----
messageSend
	^self messageSendSelector: gdb currentBytecode selector value!

----- Method: MTHostCallback>>primitive (in category 'surgery') -----
primitive
	self primitiveProcessor
		processPrimitive: gdb currentBytecode selector value
		!

----- Method: MTHostCallback>>primitiveProcessor (in category 'accessing') -----
primitiveProcessor
	primitiveProcessor isNil ifTrue: [ primitiveProcessor := PrimitiveProcessor gdb: gdb ].
	^primitiveProcessor !

----- Method: MTHostCallback>>privatePrimitive (in category 'accessing') -----
privatePrimitive
	self privatePrimitiveProcessor
		processPrimitive: gdb currentBytecode selector value
		!

----- Method: MTHostCallback>>privatePrimitiveProcessor (in category 'accessing') -----
privatePrimitiveProcessor
	privatePrimitiveProcessor isNil ifTrue: [ privatePrimitiveProcessor := PrivatePrimitiveProcessor gdb: gdb ].
	^privatePrimitiveProcessor !

----- Method: MTHostCallback>>process (in category 'API') -----
process
	| callNo |
	callNo := gdb getRegisters at: 'r22'. "cf. TAJWriter>>nextPutMagic:"
	callNo = 1 ifTrue: [ ^self messageSend ].
	callNo = 2 ifTrue: [ ^self primitive ].
	callNo = 3 ifTrue: [ ^self return ].
	callNo = 4 ifTrue: [ ^self privatePrimitive ].
	callNo = 5 ifTrue: [ ^self call ].
	callNo = 6 ifTrue: [ ^self handleDNU ].
	callNo = 7 ifTrue: [ ^self inspectionPoint ].
	self error: 'Unknown callback'!

----- Method: MTHostCallback>>return (in category 'surgery') -----
return
	| cm numToDrop |
	self halt.
	cm := MTRemoteCompiledMethod gdb: self pointer: (self getVRegister: #X).
	numToDrop := cm numArgs - 1 max: 0.
	numToDrop = 0 ifFalse: [ self setVRegister: #SP to: (self getVRegister: #SP) - (numToDrop * 4) ]
!

----- Method: MagicSurgeon>>gdb (in category 'accessing') -----
gdb
	^ gdb!

----- Method: MagicSurgeon>>gdb: (in category 'accessing') -----
gdb: anObject
	gdb := anObject!

----- Method: MagicSurgeon>>process (in category 'API') -----
process 
	self subclassResponsibility !

Object subclass: #RSPOneRegisterTransfer
	instanceVariableNames: 'regName width isLittleEndian regNum'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: RSPOneRegisterTransfer>>isLittleEndian (in category 'accessing') -----
isLittleEndian
	^ isLittleEndian!

----- Method: RSPOneRegisterTransfer>>isLittleEndian: (in category 'accessing') -----
isLittleEndian: anObject
	isLittleEndian := anObject!

----- Method: RSPOneRegisterTransfer>>readFrom: (in category 'reading') -----
readFrom: aStream
	| text int |
	text := aStream next: width//4.
	int := Integer readFrom: text base: 16.
	isLittleEndian ifTrue: [ int := int byteSwap32 ].
	"TODO - Different types (e.g. IEEE-754)"
	^int!

----- Method: RSPOneRegisterTransfer>>regName (in category 'accessing') -----
regName
	^ regName!

----- Method: RSPOneRegisterTransfer>>regName: (in category 'accessing') -----
regName: anObject
	regName := anObject!

----- Method: RSPOneRegisterTransfer>>regNum (in category 'accessing') -----
regNum
	^ regNum!

----- Method: RSPOneRegisterTransfer>>regNum: (in category 'accessing') -----
regNum: anObject
	regNum := anObject!

----- Method: RSPOneRegisterTransfer>>width (in category 'accessing') -----
width
	^ width!

----- Method: RSPOneRegisterTransfer>>width: (in category 'accessing') -----
width: anObject
	width := anObject!

----- Method: RSPOneRegisterTransfer>>write:to: (in category 'writing') -----
write: aDictionary to: aStream
	| value bytes |
	value := aDictionary at: self regName.
	isLittleEndian ifTrue: [ value := value byteSwap32 ].
	bytes := value printStringBase: 16 length: self width // 4 padded: true.
	aStream nextPutAll: bytes!

Object subclass: #RemoteGDBTransport
	instanceVariableNames: 'socket'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

RemoteGDBTransport subclass: #RemoteGDB
	instanceVariableNames: 'packetSize processorDescription tStatus why supported vCont'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: RemoteGDB class>>host:port:processorDescription: (in category 'instance creation') -----
host: h port: p processorDescription: pd
	"Create an instance, connect to the remote server, and fully prepare the debugging session."
	^(self host: h port: p)
		processorDescription: pd;
		prepareSession;
		yourself!

----- Method: RemoteGDB>>analyzeContinueAnswer: (in category 'stop codes') -----
analyzeContinueAnswer: answer
	answer isEmpty ifTrue: [ self error: 'Empty Stop-Reply packet' ].
	answer first = $S ifTrue: [ ^self analyzeContinueAnswerS: answer allButFirst ].
	answer first = $T ifTrue: [ ^self analyzeContinueAnswerT: answer allButFirst ].
	answer first = $W ifTrue: [ ^self inferiorExited: answer ].
	"Something unknown / as-yet-unimplemented"
	self error: answer!

----- Method: RemoteGDB>>analyzeContinueAnswerS: (in category 'stop codes') -----
analyzeContinueAnswerS: answer
	"The program received signal number AA (a two-digit hexadecimal number).
	This is equivalent to a T response with no n:r pairs."
	^DebugStopped onSignalNum: (self signalNumberFrom: answer)
!

----- Method: RemoteGDB>>analyzeContinueAnswerT: (in category 'stop codes') -----
analyzeContinueAnswerT: answer
	| signal textPairs importantRegs thread core stopReason stopArgument |
	signal := self signalNumberFrom: answer.
	textPairs := answer copyFrom: 3 to: answer size.
	textPairs := textPairs findTokens: ';'.
	importantRegs := Dictionary new.
	core := nil.
	thread := nil.
	stopReason := nil.
	stopArgument := nil.
	textPairs do: [ :textPair |
		| pair k v |
		pair := textPair findTokens: ':'.
		k := pair first.
		v := pair second.
		(self recognizedStopCodes includes: k)
			ifTrue: [ stopReason := k. stopArgument := v ] ifFalse: [
		k = 'thread' ifTrue: [ thread := v ] ifFalse: [
		k = 'core' ifTrue: [ core := v ] ifFalse: [
		importantRegs at: k put: (Integer readFrom: k base: 16)
		"TODO: Missing the reserved case;
		this will be handled by catching exception in #readFrom: and discarding it"
	]]]].
	self shouldBeImplemented "In TAM, this simply returns; processing is done in the sender after this call."
!

----- Method: RemoteGDB>>c (in category 'RSP commands') -----
c
	" Continue. "
	| answer |
	answer := self q: 'c'.  "NB: on some platforms, we have wanted 'vCont;c'.  I forgot why."
	^self analyzeContinueAnswer: answer!

----- Method: RemoteGDB>>decodeGPacket: (in category 'private') -----
decodeGPacket: aStream
	| registerTransfers regs |
	registerTransfers := processorDescription regsInGPacket.
	regs := Dictionary new.
	registerTransfers do: [ :rt |
		| nextValue |
		aStream atEnd ifTrue: [ ^regs ].
		nextValue := rt readFrom: aStream.
		regs at: rt regName put: nextValue ].
	^regs

!

----- Method: RemoteGDB>>getRegisters (in category 'RSP commands') -----
getRegisters
	| answer stream |
	answer := self q: 'g'.
	stream := ReadStream on: answer
		from: 1
		to: answer size.
	^self decodeGPacket: stream!

----- Method: RemoteGDB>>gtInspectorPreviewIn: (in category 'private') -----
gtInspectorPreviewIn: composite
	<gtInspectorPresentationOrder: 20>
	composite morph
		title: 'Registers';
		morph: [ GdbRegistersMorph on: self ]!

----- Method: RemoteGDB>>inferiorExited: (in category 'stop codes') -----
inferiorExited: fullAnswer 
	| exitCode |
	exitCode := Integer readFrom: fullAnswer copyWithoutFirst base: 16.
	InferiorExited signalWithExitCode: exitCode !

----- Method: RemoteGDB>>insertHWBreakpointAt: (in category 'general query commands') -----
insertHWBreakpointAt: addr
	| answer |
	answer := self q: 'Z1,',
		addr printStringHex,
		',4'.
	answer = 'OK' ifFalse: [ self error ]!

----- Method: RemoteGDB>>insertSWBreakpointAt: (in category 'general query commands') -----
insertSWBreakpointAt: addr
	| answer |
	answer := self q: 'Z1,',
		addr printStringHex,
		',4'.
	answer = 'OK' ifFalse: [ self error ]!

----- Method: RemoteGDB>>kill (in category 'RSP commands') -----
kill
	[ | answer |
	answer := self q: 'k'.
	answer = 'OK' ifFalse: [ self error: answer ].
	^self ] on: ConnectionClosed do: [ ^self ]
!

----- Method: RemoteGDB>>nameForInspector (in category 'private') -----
nameForInspector
	^'GDB'!

----- Method: RemoteGDB>>packetSize (in category 'general query commands') -----
packetSize
	packetSize isNil ifTrue: [ 
		| ps |
		ps := supported detect: [ :q | q beginsWith: 'PacketSize=' ].
		ps := ps copyFrom: 12 to: ps size.
		packetSize := SmallInteger readFrom: ps base: 16 ].
	^packetSize !

----- Method: RemoteGDB>>processorDescription (in category 'private') -----
processorDescription
	^processorDescription!

----- Method: RemoteGDB>>processorDescription: (in category 'private') -----
processorDescription: descr
	processorDescription := descr!

----- Method: RemoteGDB>>q: (in category 'private') -----
q: q
	^self
		send: q;
		receive!

----- Method: RemoteGDB>>qOffsets (in category 'general query commands') -----
qOffsets
	^self q: 'qOffsets'!

----- Method: RemoteGDB>>qStatus (in category 'general query commands') -----
qStatus
	^self q: 'qStatus'!

----- Method: RemoteGDB>>qSupported (in category 'general query commands') -----
qSupported
	supported := self q: 'qSupported:swbreak+'.
	supported isEmpty ifTrue: [ ^self error: 'qSupported returned empty string' ].
	supported := supported findTokens: ';'!

----- Method: RemoteGDB>>qTStatus (in category 'general query commands') -----
qTStatus
	tStatus := self q: 'qTStatus'!

----- Method: RemoteGDB>>recognizedStopCodes (in category 'stop codes') -----
recognizedStopCodes
	^#(
	'watch' 'rwatch' 'awatch'
	'syscall_entry' 'syscall_return'
	'library' 'replaying'
	'swbreak'	'hwbreak'
	'fork' 'vfork' 	'vforkdone'
	'exec' 'create'
	)!

----- Method: RemoteGDB>>s (in category 'RSP commands') -----
s
	"Single step.
	Return control with signal 5 (TRAP),
	or if the execution of the current instruction failed, with whatever signal happened."
	| answer |
	answer := self q: 's'.
	^self analyzeContinueAnswer: answer
!

----- Method: RemoteGDB>>setRegisters: (in category 'RSP commands') -----
setRegisters: aDict
	| answer stream registerTransfers |
	stream := WriteStream on: String new.
	registerTransfers := processorDescription regsInGPacket.
	registerTransfers do: [ :rt |
		rt write: aDict to: stream ].
	
	answer := self q: 'G', stream contents.	
	answer = 'OK' ifFalse: [self error: answer]!

----- Method: RemoteGDB>>setThread:t: (in category 'RSP commands') -----
setThread: c t: t
	" Hxyyyy family of commands (e.g. Hc-1 or Hg0). "
	| answer |
	answer := self q: 'H', c, t printStringHex.
	answer = 'OK' "ifFalse: [ self error: answer ]" "I've seen this happen in normal operation of the GNU GDB and silently ignored"
!

----- Method: RemoteGDB>>signalNumberFrom: (in category 'stop codes') -----
signalNumberFrom: answer
	^Integer readFrom: (answer copyFrom: 1 to: 2) base: 16
!

----- Method: RemoteGDB>>vCont (in category 'RSP commands') -----
vCont
	"Stepping mechanisms supported by the server"
	vCont := self q: 'vCont?'!

----- Method: RemoteGDB>>why (in category 'RSP commands') -----
why
	why := self q: '?'!

----- Method: RemoteGDB>>writeBytes:toAddr: (in category 'RSP commands') -----
writeBytes: aByteArray toAddr: addr
	| textualAddr currAddr answer |
	currAddr := addr.
	aByteArray do: [ :byte |
		| data |
		data := byte printStringBase: 16 length: 2 padded: true.
		textualAddr := currAddr printStringBase: 16 length: 8 padded: true.
		answer := self
			q: 'M', textualAddr,  ',1:', data.
		answer = 'OK' ifFalse: [ self error: answer ].
		currAddr := currAddr + 1
	]!

RemoteGDB subclass: #RemoteGDBSession
	instanceVariableNames: 'qXfer vFile'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

RemoteGDBSession subclass: #ExecutableAwareGDB
	instanceVariableNames: 'exe'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-TAJ'!

----- Method: ExecutableAwareGDB>>currentBytecode (in category 'representation') -----
currentBytecode
	| def vPC |
	def := self exe whereAmI.
	vPC := self getVRegister: #VPC.
	^def ir instructions at: vPC+1!

----- Method: ExecutableAwareGDB>>currentFrame (in category 'representation') -----
currentFrame
	^MTRemoteStackFrame gdb: self pointer: (self getVRegister: #FP)!

----- Method: ExecutableAwareGDB>>exe (in category 'accessing') -----
exe
	^ exe!

----- Method: ExecutableAwareGDB>>exe: (in category 'accessing') -----
exe: anObject
	exe := anObject!

----- Method: ExecutableAwareGDB>>findPreviousCallers:from: (in category 'debug support') -----
findPreviousCallers: maxCallersToFind from: spOrNil
	| sp |
	maxCallersToFind < 1 ifTrue: [ ^OrderedCollection new ].
	sp := spOrNil isNil ifTrue: [ self getVRegister: #SP ] ifFalse: [ spOrNil ].
	[ self isInStack: sp ] whileTrue: [ 
		| slot |
		slot := self read32At: sp.
		sp := sp - 4.
		(self isInNZone: slot) ifTrue: [ ^(self findPreviousCallers: maxCallersToFind - 1 from: sp) addFirst: (exe whereIsPC: slot); yourself ]
	].
	^OrderedCollection new!

----- Method: ExecutableAwareGDB>>getVRegister: (in category 'representation') -----
getVRegister: r
	| nRegister |
	nRegister := TAJWriter vRegister: r.
	^self getRegisters at: 'r', nRegister printString!

----- Method: ExecutableAwareGDB>>isInNZone: (in category 'debug support') -----
isInNZone: anAddress
	^(anAddress < 272699392) and: [ anAddress > 268505088 ]!

----- Method: ExecutableAwareGDB>>isInStack: (in category 'debug support') -----
isInStack: anAddress
	^(anAddress >= 272699392) and: [ anAddress < 273747968 ]!

----- Method: ExecutableAwareGDB>>isMTNil: (in category 'representation') -----
isMTNil: anOop
	^anOop = (self exe externalReferences at: #MT_nil)!

----- Method: ExecutableAwareGDB>>mdFor: (in category 'representation') -----
mdFor: oop
	| tagBits   |
	tagBits := oop bitAnd: 3.
	tagBits == 1 ifTrue: [
		"SmallInteger"
		^exe externalReferences at: #MTSmallInteger_md ].
	tagBits == 3 ifTrue: [ 
		"Context"
		^exe externalReferences at: #MTContext_md ].
	^self read32At: oop + 4!

----- Method: ExecutableAwareGDB>>pharoObjectForOop: (in category 'representation') -----
pharoObjectForOop: oop
	| tag |
	tag := oop bitAnd: 2r111.
	tag == 0 ifTrue: [ ^MTRemoteObject gdb: self pointer: oop ].
	tag == 1 ifTrue: [ ^exe architecture oop >> 4 ]. "SmallInteger; this code better delegate to the Target"
	tag == 3 ifTrue: [ ^MTRemoteObject gdb: self pointer: (oop bitAnd: 16rFFFFFFF0) ].
	self halt "I don't know what tag this is"!

----- Method: ExecutableAwareGDB>>pharoObjectForVReg: (in category 'representation') -----
pharoObjectForVReg: vReg
	^self pharoObjectForOop: (self getVRegister: vReg)!

----- Method: ExecutableAwareGDB>>processCallback (in category 'surgery') -----
processCallback
	(MTHostCallback gdb: self) process
		!

----- Method: ExecutableAwareGDB>>push: (in category 'representation') -----
push: oop
	| sp |
	sp := self getVRegister: #SP.
	self writeInt32: oop toAddr: sp+4.
	self setVRegister: #SP to: sp+4!

----- Method: ExecutableAwareGDB>>setVRegister:to: (in category 'representation') -----
setVRegister: r to: x
	| nRegister |
	nRegister := TAJWriter vRegister: r.
	self setRegister: nRegister to: x!

----- Method: ExecutableAwareGDB>>tryRunning (in category 'surgery') -----
tryRunning
	self runWithMagickCallback: [ self processCallback ]!

----- Method: ExecutableAwareGDB>>tryRunningSteps (in category 'surgery') -----
tryRunningSteps
	self runStepsWithMagickCallback: [ self processCallback ]!

----- Method: RemoteGDBSession>>advancePastTrap (in category 'magick') -----
advancePastTrap
	| regs |
	regs := self getRegisters.
	regs at: 'pc' put: (regs at: 'pc') + 4.
	self setRegisters: regs.!

----- Method: RemoteGDBSession>>askFeatures (in category 'RSP commands') -----
askFeatures
	" Ask the stub for the target feature descriptor, and return it.
	  If the stub doesn't provide it, return nil. "
	self supportsFeatures
		ifTrue: [ ^self qXfer features: 'target.xml' ]
		ifFalse: [ ^nil ]
		!

----- Method: RemoteGDBSession>>currentInstruction (in category 'RSP commands') -----
currentInstruction
	^processorDescription disassemble: self currentInstructionEncoding
!

----- Method: RemoteGDBSession>>getRegister: (in category 'accessing') -----
getRegister: r
	| regs |
	regs := self getRegisters.
	^regs at: r!

----- Method: RemoteGDBSession>>pc (in category 'RSP commands') -----
pc
	^self getRegisters at: self processorDescription pcRegisterName !

----- Method: RemoteGDBSession>>pc: (in category 'RSP commands') -----
pc: newPC
	^self setRegister: self processorDescription pcRegisterName to: newPC!

----- Method: RemoteGDBSession>>prepareSession (in category 'RSP commands') -----
prepareSession
	self qSupported.
	self packetSize.
	self setThread: 'g' t: 0.
	self qStatus = '' ifFalse: [ self error: 'Bad status' ].
	self vCont.

	self askFeatures ifNotNil: [ :xml | processorDescription regsInGPacket: (GdbXmlParser new processXML: xml) ]
!

----- Method: RemoteGDBSession>>printRegistersOn: (in category 'printing') -----
printRegistersOn: aStream
	| allRegisters |
	allRegisters := self getRegisters.
	allRegisters keysAndValuesDo: [ :regName :regValue |
		(#('ds' 'ss' 'cs' 'es' 'fs' 'gs') includes: regName) ifFalse: [
			aStream nextPutAll: regName; nextPutAll: ': '.
			regValue printOn: aStream base: 16 length: 8 padded: true.
			aStream cr	
	]]!

----- Method: RemoteGDBSession>>qXfer (in category 'RSP Helpers') -----
qXfer
	qXfer isNil ifTrue: [ qXfer := RemoteGdbXFER gdb: self ].
	^ qXfer!

----- Method: RemoteGDBSession>>runStepsWithMagickCallback: (in category 'magick') -----
runStepsWithMagickCallback: magicCallbackBlock
	[ true ] whileTrue: [ self stepUntilMagick. magicCallbackBlock value ] !

----- Method: RemoteGDBSession>>runUntil: (in category 'RSP commands') -----
runUntil: aBlock
	[ self c ] doWhileFalse: aBlock!

----- Method: RemoteGDBSession>>runUntilMagick (in category 'magick') -----
runUntilMagick
	self runUntil: [ self currentInstructionEncoding = (Integer readFrom: self exe objectMemory targetSetup magicInstruction radix: 2) ]!

----- Method: RemoteGDBSession>>runWithMagickCallback: (in category 'magick') -----
runWithMagickCallback: magicCallbackBlock
	[ true ] whileTrue: [ self runUntilMagick. magicCallbackBlock value ] !

----- Method: RemoteGDBSession>>setRegister:to: (in category 'accessing') -----
setRegister: r to: x
	| regs |
	regs := self getRegisters.
	regs at: r put: x.
	self setRegisters: regs!

----- Method: RemoteGDBSession>>stepUntil: (in category 'RSP commands') -----
stepUntil: aBlock
	[ self s.
	Transcript show: ((self getRegisters at: 'r19') printString); cr.
	] doWhileFalse: aBlock!

----- Method: RemoteGDBSession>>stepUntilMagick (in category 'magick') -----
stepUntilMagick
	self stepUntil: [ self currentInstructionEncoding = self exe objectMemory targetSetup magicInstruction ]!

----- Method: RemoteGDBSession>>supportsFeatures (in category 'RSP commands') -----
supportsFeatures
	" Answer whether the stub supports target feature descriptors. "
	| features |
	features := supported detect: [ :s | s beginsWith: 'qXfer:features:' ] ifNone: [ ^false ].
	features = 'qXfer:features:read+' ifFalse: [ self error: 'Havent encountered such a CPU yet; investigate' ].
	"In reality the following line must be, ^true."
	^false "disabled for now, before we have xi:include in the XML parser"
!

----- Method: RemoteGDBSession>>vFile (in category 'RSP Helpers') -----
vFile
	vFile isNil ifTrue: [ vFile := RemoteGdbVFILE gdb: self ].
	^ vFile!

----- Method: RemoteGDBTransport class>>host: (in category 'as yet unclassified') -----
host: h
	^self host: h port: self wellKnownPort !

----- Method: RemoteGDBTransport class>>host:port: (in category 'as yet unclassified') -----
host: h port: p
	| socket |
	socket := Socket newTCP 
		connectTo: (NetNameResolver addressForName: h) port: p.
	^self onSocket: socket!

----- Method: RemoteGDBTransport class>>onSocket: (in category 'as yet unclassified') -----
onSocket: aSocket
	^self new socket: aSocket !

----- Method: RemoteGDBTransport class>>wellKnownPort (in category 'as yet unclassified') -----
wellKnownPort
	^2159!

----- Method: RemoteGDBTransport>>assemblePacket: (in category 'private') -----
assemblePacket: packetDataString
	| s cksum |
	s := '$', (self escape: packetDataString), '#'.
	cksum := packetDataString  inject: 0 into: [ :soFar :c | soFar + c asciiValue \\ 256 ].
	^s, (cksum printStringBase: 16 nDigits: 2) asLowercase 
!

----- Method: RemoteGDBTransport>>disconnect (in category 'target connection') -----
disconnect
	self socket close!

----- Method: RemoteGDBTransport>>escape: (in category 'private') -----
escape: aString
	^aString inject: '' into: [ :soFar :c | soFar, (self escapeChar: c) ].
	!

----- Method: RemoteGDBTransport>>escapeChar: (in category 'private') -----
escapeChar: c
	"Where the characters '#' or '$' appear in the packet data,
	they must be escaped.
	The escape character is ASCII 0x7d ('}'),
	and is followed by the original character XORed with 0x20.
	The character '}' itself must also be escaped."
	
	((c == $# or: [c == $$]) or: [c == $}]) ifTrue: [ 
		^String with: $} with: (Character value: (c asciiValue bitXor: 16r20))
	] ifFalse: [ ^String with: c ]!

----- Method: RemoteGDBTransport>>readAndVerifyCksum: (in category 'private') -----
readAndVerifyCksum: anInteger
	"Read next two characters from the TCP stream and verify that they represent the same hext number as anInteger."
	| cksumFromServer |

	cksumFromServer := Integer
		readFrom: (String with: self receiveByte with: self receiveByte)
		radix: 16.
	"cksumFromServer = anInteger ifFalse: [ self error: 'Wrong checksum' ]"!

----- Method: RemoteGDBTransport>>receive (in category 'API') -----
receive
	| c stream cksum cc |
	"Receive and decode the server's response.
	It may come in chunks on the TCP stream; in that sense, the word Packet may be confusing."
	
	self receiveByte = $$ ifFalse: [ self error: 'Packet does not start with $' ].

	stream := WriteStream with: ''.
	cksum := 0.
	[ c := self receiveChar. c = $# ] whileFalse: [
		cksum := cksum + c asInteger \\ 256.
		c = $* ifTrue: [
			| nChar |
			nChar := self receiveChar asciiValue.
			cksum := cksum + nChar \\ 256.
			nChar - 29 timesRepeat: [ stream nextPut: cc.  ]
		"RLE"
		 ] ifFalse: [ stream nextPut: c. cc := c. ]].
"TODO: escaping"
	self readAndVerifyCksum: cksum.
	socket sendData: '+'. 

	^stream contents
!

----- Method: RemoteGDBTransport>>receiveByte (in category 'API') -----
receiveByte
	| buf1 |
	"Receive exactly one byte, waiting for it if neccesary.
	This is at a level lower than escaping; see #receiveChar for the escaped version."
	buf1 := ByteString new: 1.
	(socket receiveDataInto: buf1) == 1 ifFalse: [GdbChildExited new signal].
	^buf1 first!

----- Method: RemoteGDBTransport>>receiveChar (in category 'API') -----
receiveChar
	"Receive the next char from the TCP stream."

	| byte |
	byte := self receiveByte.
	"Do NOT escape at this level; check for RLE, and then escaping is above."
	^byte!

----- Method: RemoteGDBTransport>>send: (in category 'API') -----
send: aString
	| packet ack n |
	packet := self assemblePacket: aString.
	socket sendData: packet.
	ack := '*'.
	n := socket receiveDataInto: ack.
	ack = '+' ifFalse: [ self error: 'gdb server answered ', ack ]
!

----- Method: RemoteGDBTransport>>socket (in category 'accessing') -----
socket
	^ socket!

----- Method: RemoteGDBTransport>>socket: (in category 'accessing') -----
socket: aSocket
	socket := aSocket!

Object subclass: #RemoteGdbAbstractHelper
	instanceVariableNames: 'gdb'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: RemoteGdbAbstractHelper class>>gdb: (in category 'instance creation') -----
gdb: aRemoteGDB
	^self new
		gdb: aRemoteGDB ;
		yourself !

----- Method: RemoteGdbAbstractHelper>>gdb (in category 'accessing') -----
gdb
	^ gdb!

----- Method: RemoteGdbAbstractHelper>>gdb: (in category 'accessing') -----
gdb: anObject
	gdb := anObject!

RemoteGdbAbstractHelper subclass: #RemoteGdbVFILE
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

RemoteGdbAbstractHelper subclass: #RemoteGdbXFER
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: RemoteGdbXFER>>features: (in category 'as yet unclassified') -----
features: filename
	| answer |
	answer := self readFeatureDocument: filename.
	answer isEmpty ifTrue: [ ^nil ]. " stub has indicated he does not understand this request "
	answer first == $l ifFalse: [ ^self error ].
	answer := answer allButFirst.
	^self parseFeatures: answer!

----- Method: RemoteGdbXFER>>parseFeatures: (in category 'as yet unclassified') -----
parseFeatures: aStringOfXML
	self shouldBeImplemented 
	
	"The below code used to work,
	back when we had PPXmlParser.
	
	
	| xml gdbXmlParser |
	
	gdbXmlParser := GdbXmlParser endian: gdb processorDescription endian.
	gdbXmlParser parseString: aStringOfXML.
	self halt.
	xml baseURI: (RemoteGdbFeatureURIProvider gdb: gdb).
	^xml rootElement copy"!

----- Method: RemoteGdbXFER>>readFeatureDocument: (in category 'as yet unclassified') -----
readFeatureDocument: filename
	^gdb q: 'qXfer:features:read:', filename, ':0,fff'!

Object subclass: #RemoteGdbFeatureURIProvider
	instanceVariableNames: 'gdb'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: RemoteGdbFeatureURIProvider class>>gdb: (in category 'as yet unclassified') -----
gdb: aGDB
	^self new
		gdb: aGDB;
		yourself !

----- Method: RemoteGdbFeatureURIProvider>>gdb (in category 'accessing') -----
gdb
	^ gdb!

----- Method: RemoteGdbFeatureURIProvider>>gdb: (in category 'accessing') -----
gdb: anObject
	gdb := anObject!

----- Method: RemoteGdbFeatureURIProvider>>get: (in category 'API') -----
get: filename
	^gdb qXfer features: filename!

Object subclass: #ShmemUFFI
	instanceVariableNames: 'ptr'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

!ShmemUFFI commentStamp: 'BorisShingarov 5/23/2019 04:12' prior: 0!
FFI interface to Shared Memory.!

----- Method: ShmemUFFI class>>allocate: (in category 'NB interface to SHM') -----
allocate: nBytes
	| ptr |
	ptr := self shmaddr: nBytes.
	^ShmemUFFI new 
		ptr: ptr;
		yourself !

----- Method: ShmemUFFI class>>shmaddr: (in category 'NB interface to SHM') -----
shmaddr: size
"   ShmemUFFI shmaddr: 1024000   "
	
	^ self
		ffiCall: #( void* shmaddr (int size) )
		module: self soName!

----- Method: ShmemUFFI class>>soName (in category 'NB interface to SHM') -----
soName
	^'/home/boris/work/thinshell/HelloNB.so'!

----- Method: ShmemUFFI>>ptr (in category 'accessing') -----
ptr
	^ ptr!

----- Method: ShmemUFFI>>ptr: (in category 'accessing') -----
ptr: anObject
	ptr := anObject!

Object subclass: #SimulationAddressSpace
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

SimulationAddressSpace subclass: #RemoteRAM
	instanceVariableNames: 'gdb'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

RemoteRAM subclass: #BufferingRemoteRAM
	instanceVariableNames: 'buffer minAddr leftFinger queuedBreakpoints'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: BufferingRemoteRAM>>buffer (in category 'accessing') -----
buffer
	buffer isNil ifTrue: [ buffer := ByteArray new: (4*1024*1024) + (1024*1024) + (80*1024*1024) ].
	^buffer!

----- Method: BufferingRemoteRAM>>chunkSize (in category 'flushing') -----
chunkSize
	"In bytes"
	^2048 "maybe? this was always 4096, I have no idea why this started breaking in Maribor"!

----- Method: BufferingRemoteRAM>>flushRAM (in category 'flushing') -----
flushRAM
	| firstUnallocatedAddress |
	firstUnallocatedAddress := self getVRegister: #HEAP.
	leftFinger := 1.
	'Injecting program'
		displayProgressFrom: minAddr to: firstUnallocatedAddress during:
			[ :bar | 	self transmitBelow: firstUnallocatedAddress updating: bar ]
!

----- Method: BufferingRemoteRAM>>insertSWBreakpointAt: (in category 'API') -----
insertSWBreakpointAt: addr
	self queuedBreakpoints add: addr
 		
 
!

----- Method: BufferingRemoteRAM>>minAddr (in category 'accessing') -----
minAddr
	minAddr isNil ifTrue: [ minAddr := self getVRegister: #NZone ].
	^minAddr!

----- Method: BufferingRemoteRAM>>queuedBreakpoints (in category 'accessing') -----
queuedBreakpoints
	queuedBreakpoints isNil ifTrue: [ queuedBreakpoints := OrderedCollection new ].
	^queuedBreakpoints!

----- Method: BufferingRemoteRAM>>read32At: (in category 'API') -----
read32At: addr
	leftFinger isNil ifTrue: [
		^(((self buffer at: addr - self minAddr + 1) << 24
		bitOr: (self buffer at: addr - self minAddr + 2) << 16)
		bitOr: (self buffer at: addr - self minAddr + 3) << 8)
		bitOr: (self buffer at: addr - self minAddr + 4)
		 ].
	^super read32At: addr!

----- Method: BufferingRemoteRAM>>transmitBelow:updating: (in category 'flushing') -----
transmitBelow: firstUnallocatedAddress updating: aProgressBar
| bytes thisChunk |
	bytes := firstUnallocatedAddress - self minAddr.
	bytes = 0 ifTrue: [ buffer := nil. ^self ].
	bytes > self chunkSize ifTrue: [ bytes := self chunkSize ].

thisChunk := buffer copyFrom: leftFinger to: leftFinger + bytes - 1.
aProgressBar value: minAddr.
self writeBytes: thisChunk toAddr: minAddr.
minAddr := minAddr + bytes.
leftFinger := leftFinger + bytes.
self transmitBelow: firstUnallocatedAddress updating: aProgressBar !

----- Method: BufferingRemoteRAM>>writeBytes:toAddr: (in category 'API') -----
writeBytes: aByteArray toAddr: addr
	| start stop |
	leftFinger isNil ifTrue: [
		start := addr - self minAddr + 1.
		stop := start + aByteArray size - 1.
		^self buffer replaceFrom: start to: stop with: aByteArray startingAt: 1 ].
	^super writeBytes: aByteArray toAddr: addr!

----- Method: BufferingRemoteRAM>>writeInt32:toAddr: (in category 'API') -----
writeInt32: int toAddr: addr
	leftFinger isNil ifTrue: [
		^self buffer
			at: addr - self minAddr + 1 put: ((int bitAnd: 16rFF000000) >> 24);
			at: addr - self minAddr + 2 put: ((int bitAnd: 16r00FF0000) >> 16);
			at: addr - self minAddr + 3 put: ((int bitAnd: 16r0000FF00) >> 8);
			at: addr - self minAddr + 4 put: (int bitAnd: 16r000000FF) ].
	super writeInt32: int toAddr: addr
!

RemoteRAM subclass: #Gem5SharedRAM
	instanceVariableNames: 'tlb hostPtr shmemSize'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: Gem5SharedRAM class>>gem5: (in category 'FFI') -----
gem5: nBytes
	"Answer the void* pointer to the backing store of the gem5 guest memory."
	| fd addr |
	fd := self shmOpen: '/gem5'
		with: 64"O_CREAT" | 2"O_RDWR"
		with: 8r666. "mode"
	addr := self mmap: 0
		with: nBytes
		with: 1"PROT_READ" | 2"PROT_WRITE"
		with: 1 "MAP_SHARED"
		with: fd
		with: 0.
	^addr

"
Gem5SharedRAM halt; gem5: 5000
"
!

----- Method: Gem5SharedRAM class>>mmap:with:with:with:with:with: (in category 'FFI') -----
mmap: addr with: length with: prot with: flags with: fd with: offset
	<cdecl: void* 'mmap' (longlong longlong long long long longlong) module: '/lib/x86_64-linux-gnu/libc.so.6'>
	^self externalCallFailed
!

----- Method: Gem5SharedRAM class>>mmuPageSize (in category 'granularity') -----
mmuPageSize
	^4096!

----- Method: Gem5SharedRAM class>>offsetMask (in category 'granularity') -----
offsetMask
	^self mmuPageSize - 1!

----- Method: Gem5SharedRAM class>>pageMask (in category 'granularity') -----
pageMask
	^16rFFFFFFFF bitXor: self offsetMask
!

----- Method: Gem5SharedRAM class>>shmOpen:with:with: (in category 'FFI') -----
shmOpen: name with: oflag with: mode
	<cdecl: ulong 'shm_open' (char* ulong ulong) module: '/lib/x86_64-linux-gnu/librt.so.1'>
	^self externalCallFailed
!

----- Method: Gem5SharedRAM>>byteAtAddr:put: (in category 'writing') -----
byteAtAddr: byteAddress put: byte
	| ptr |
byteAddress = 16r109014 ifTrue: [self halt].
	ptr := self translate: byteAddress.
	ptr unsignedByteAt: 1 put: byte!

----- Method: Gem5SharedRAM>>fillFromStream:startingAt:count: (in category 'writing') -----
fillFromStream: aFileStream startingAt: startAddress count: count
	| contents |
	contents := aFileStream next: count.
	self writeBytes: contents toAddr: startAddress.
	^contents size!

----- Method: Gem5SharedRAM>>forceRead32At: (in category 'address translation') -----
forceRead32At: addr
	| x |
	self halt. "I don't remember why this was needed."
	x := super read32At: addr.
	tlb := self getTLB.
	^x
!

----- Method: Gem5SharedRAM>>forceReadAt:nBytes: (in category 'address translation') -----
forceReadAt: addr nBytes: n
	super readAt: addr nBytes: n.
	tlb := self getTLB!

----- Method: Gem5SharedRAM>>forceWriteBytes:toAddr: (in category 'address translation') -----
forceWriteBytes: aByteArray  toAddr: addr
	| x |
	x := super writeBytes: aByteArray  toAddr: addr.
	tlb := self getTLB.
	^x
!

----- Method: Gem5SharedRAM>>forceWriteInt32:toAddr: (in category 'address translation') -----
forceWriteInt32: int toAddr: addr
	| x |
	x := super writeInt32: int toAddr: addr.
	tlb := self getTLB.
	^x
!

----- Method: Gem5SharedRAM>>getTLB (in category 'RSP') -----
getTLB
	| answer |
	answer := self gdb q: '.'.
	answer isEmpty ifTrue: [ self error: 'GDB failed to return TLB' ].
	^Dictionary newFromAssociations:
		(((answer findTokens: ';')
		collect: [ :s | s findTokens: ':' ])
		collect: [ :pair | (Integer readFrom: pair first base: 16) -> (Integer readFrom: pair last base: 16) ])!

----- Method: Gem5SharedRAM>>hostPtr (in category 'shmem') -----
hostPtr
	hostPtr isNil ifTrue: [ hostPtr := Gem5SharedRAM gem5: shmemSize ].
	^hostPtr!

----- Method: Gem5SharedRAM>>longAtAddr:put:bigEndian: (in category 'writing') -----
longAtAddr: addr put: aValue bigEndian: bigEndian
	| ptr int |
	int := bigEndian
		ifTrue: [ aValue byteSwap32 ]
		ifFalse: [ aValue ].
	ptr := self translate: addr.
	ptr unsignedLongAt: 1 put: int
	
	
	
!

----- Method: Gem5SharedRAM>>read:bytesAtAddr: (in category 'reading') -----
read: n bytesAtAddr: addr
	| backingAddr |
	backingAddr := self translate: addr.
	^((1 to: n) collect: [ :idx| backingAddr byteAt: idx ]) asByteArray
!

----- Method: Gem5SharedRAM>>shmemSize (in category 'shmem') -----
shmemSize
	^shmemSize!

----- Method: Gem5SharedRAM>>shmemSize: (in category 'shmem') -----
shmemSize: howBig
	shmemSize := howBig!

----- Method: Gem5SharedRAM>>tlb (in category 'address translation') -----
tlb
	tlb isNil ifTrue: [ tlb := self getTLB ].
	^tlb!

----- Method: Gem5SharedRAM>>translate: (in category 'address translation') -----
translate: addr
	| pageAddr |
	pageAddr := self virt2phys: addr.
	pageAddr isNil ifTrue: [ ^ self error forceRead32At: addr ].
	^self hostPtr getHandle + pageAddr.
!

----- Method: Gem5SharedRAM>>unsignedLongAtAddr:bigEndian: (in category 'reading') -----
unsignedLongAtAddr: addr bigEndian: bigEndian
	| backingAddr int |
	backingAddr := self translate: addr.
	int := backingAddr unsignedLongAt: 1.
	bigEndian ifTrue: [ int := int byteSwap32].
	^int!

----- Method: Gem5SharedRAM>>virt2phys: (in category 'address translation') -----
virt2phys: anAddress
	" Answer the physical address for the given virtual address,
	 if it is mapped, nil otherwise. "
	| pageVirt pagePhys |
	pageVirt := anAddress bitAnd: self class pageMask.
	pagePhys := self tlb at: pageVirt ifAbsent: [ ^nil ].
	^pagePhys bitOr: (anAddress bitAnd: self class offsetMask)!

----- Method: Gem5SharedRAM>>writeBytes:toAddr: (in category 'writing') -----
writeBytes: aByteArray toAddr: addr
	| ptr |
addr = 16r109014 ifTrue: [self halt].
	ptr := self translate: addr.
	aByteArray doWithIndex: [ :x :idx | ptr unsignedByteAt: idx put: x ] "is there a faster way?"
!

Gem5SharedRAM subclass: #Gem5SharedRAM8K
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-RSP'!

----- Method: Gem5SharedRAM8K class>>mmuPageSize (in category 'granularity') -----
mmuPageSize
	"GEM5 MIPS.  Sweetman claims this should not happen."
	^8192!

----- Method: RemoteRAM class>>gdb: (in category 'instance creation') -----
gdb: aRemoteGDB
	^self basicNew 
		gdb: aRemoteGDB;
		yourself !

----- Method: RemoteRAM>>byteAt: (in category 'reading') -----
byteAt: index
	"Compatibe with the ByteArray.
	Index is measured in bytes, and is 1-based.
	The return value is always unsigned."
	^self byteAtAddr: index - 1!

----- Method: RemoteRAM>>byteAt:put: (in category 'writing') -----
byteAt: index put: byte
	self byteAtAddr: index - 1 put: byte!

----- Method: RemoteRAM>>byteAtAddr: (in category 'reading') -----
byteAtAddr: addr
	^self unsignedByteAtAddr: addr!

----- Method: RemoteRAM>>byteAtAddr:put: (in category 'writing') -----
byteAtAddr: byteAddress put: byte
	| textualAddr data |
	data := byte printStringBase: 16 length: 2 padded: true.
	textualAddr := byteAddress printStringBase: 16 length: 8 padded: true.
	^ self writeBytesHex: data toAddrHex: textualAddr hexSize: '1'!

----- Method: RemoteRAM>>flush (in category 'target synchronization') -----
flush
	"Do nothing because I write directly to the target's memory"!

----- Method: RemoteRAM>>gdb (in category 'debugger access') -----
gdb
	^ gdb!

----- Method: RemoteRAM>>gdb: (in category 'debugger access') -----
gdb: anObject
	gdb := anObject!

----- Method: RemoteRAM>>long64At:put: (in category 'reading') -----
long64At: byteIndex put: aValue
	| lowBits mask wordIndex |
	(lowBits := byteIndex - 1 \\ 4) = 0 ifTrue:
		[self "N.B. Do the access that can fail first, before altering the receiver"
			longAt: byteIndex + 4 put: (aValue bitShift: -32);
			unsignedLongAt: byteIndex put: (aValue bitAnd: 16rffffffff).
		^aValue].
	"There will always be three accesses; two partial words and a full word in the middle"
	wordIndex := byteIndex - 1 // 4 + 1.
	aValue < 0
		ifTrue:
			[(aValue bitShift: -32) < -2147483648 ifTrue:
				[^self errorImproperStore]]
		ifFalse:
			[16r7FFFFFFF < (aValue bitShift: -32) ifTrue:
				[^self errorImproperStore]].
	mask := 16rFFFFFFFF bitShift: 4 - lowBits * -8.
	self at: wordIndex put: (((self at: wordIndex) bitAnd: mask) bitXor: ((aValue bitShift: lowBits * 8) bitAnd: mask bitInvert32)).
	self at: wordIndex + 1 put: ((aValue bitShift: 4 - lowBits * -8) bitAnd: 16rFFFFFFFF).
	self at: wordIndex + 2 put: (((self at: wordIndex + 2) bitAnd: mask bitInvert32) bitXor: ((aValue bitShift: 4 - lowBits + 4 * -8) bitAnd: mask)).
	^aValue!

----- Method: RemoteRAM>>longAt:put: (in category 'writing') -----
longAt: byteIndex put: int
	^self unsignedLongAt: byteIndex put: int 
!

----- Method: RemoteRAM>>longAt:put:bigEndian: (in category 'writing') -----
longAt: byteIndex put: aValue bigEndian: bigEndian
	"Compatibility with the ByteArray method of the same name."
	self longAtAddr: byteIndex  - 1 put: aValue bigEndian: bigEndian!

----- Method: RemoteRAM>>longAtAddr:put:bigEndian: (in category 'writing') -----
longAtAddr: addr put: aValue bigEndian: bigEndian
	"Compatibility with the ByteArray method of the same name."
	| textualAddr data textualData |
	textualAddr := addr printStringBase: 16 length: 8 padded: true.
	data := bigEndian
		ifTrue: [ aValue ]
		ifFalse: [ aValue byteSwap32 ].
	textualData := data printStringBase: 16 length: 8 padded: true.
	self writeBytesHex: textualData
		toAddrHex: textualAddr
		hexSize: '4'!

----- Method: RemoteRAM>>read:bytesAsHexAt: (in category 'RSP protocol') -----
read: n bytesAsHexAt: addr
	"Answer the hex string the gdbserver will return to represent the n bytes read from address addr.
	All read operations are implemented on top of this primitive."
	^self gdb q: 'm',
		addr printStringHex,
		',',
		n printStringHex
!

----- Method: RemoteRAM>>read:bytesAt: (in category 'reading') -----
read: n bytesAt: index
	^self read: n bytesAtAddr: index - 1!

----- Method: RemoteRAM>>read:bytesAtAddr: (in category 'reading') -----
read: n bytesAtAddr: addr
	| answer stream |
	answer := self read: n bytesAsHexAt: addr.
	stream := ReadStream on: answer
		from: 1
		to: answer size.
	answer := ByteArray new: n.
	1 to: n do: [ :idx |
		| b |
		b := stream next: 2.
		answer at: idx put: (Integer readFrom: b base: 16) ].
	^answer!

----- Method: RemoteRAM>>readInt32fromAddr: (in category 'remote endian') -----
readInt32fromAddr: addr
	"Read, using the REMOTE TARGET endianness."
	^self unsignedLongAtAddr: addr bigEndian: self isBigEndian!

----- Method: RemoteRAM>>signedLong64At: (in category 'reading') -----
signedLong64At: byteIndex
	^(self unsignedLong64At: byteIndex) signedIntFromLong64
!

----- Method: RemoteRAM>>unsignedByteAt: (in category 'reading') -----
unsignedByteAt: index
	^self unsignedByteAtAddr: index - 1!

----- Method: RemoteRAM>>unsignedByteAtAddr: (in category 'reading') -----
unsignedByteAtAddr: addr
	^(self read: 1 bytesAtAddr: addr) first!

----- Method: RemoteRAM>>unsignedLong64At: (in category 'reading') -----
unsignedLong64At: byteIndex
	^self unsignedLong64AtAddr: byteIndex - 1!

----- Method: RemoteRAM>>unsignedLong64AtAddr: (in category 'reading') -----
unsignedLong64AtAddr: addr
	| hiWord loWord |
	addr \\ 8 ~= 0 ifTrue: [self unalignedAccessError].
	loWord := self unsignedLongAtAddr: addr bigEndian: false.
	hiWord := self unsignedLongAtAddr: addr + 4 bigEndian: false.
	^hiWord = 0
		ifTrue: [loWord]
		ifFalse: [(hiWord bitShift: 32) + loWord]!

----- Method: RemoteRAM>>unsignedLongAt:bigEndian: (in category 'reading') -----
unsignedLongAt: byteIndex bigEndian: bigEndian
	^self unsignedLongAtAddr: byteIndex - 1 bigEndian: bigEndian!

----- Method: RemoteRAM>>unsignedLongAt:put: (in category 'writing') -----
unsignedLongAt: byteIndex put: int 
	self longAtAddr: byteIndex - 1 put: int bigEndian: self isBigEndian!

----- Method: RemoteRAM>>unsignedLongAtAddr:bigEndian: (in category 'reading') -----
unsignedLongAtAddr: addr bigEndian: bigEndian
	| string int |
	string := self read: 4 bytesAsHexAt: addr.
	int := Integer readFrom: string radix: 16.
	bigEndian ifFalse: [ int := int byteSwap32 ].
	^int!

----- Method: RemoteRAM>>unsignedShortAtAddr:bigEndian: (in category 'reading') -----
unsignedShortAtAddr: addr bigEndian: bigEndian
	| string int |
	string := self read: 2 bytesAsHexAt: addr.
	int := Integer readFrom: string radix: 16.
	bigEndian ifFalse: [ int := int byteSwap16 ].
	^int!

----- Method: RemoteRAM>>writeBytes:toAddr: (in category 'writing') -----
writeBytes: aByteArray toAddr: addr
	| buffer textualAddr textualSize |
	buffer := WriteStream on: (String new: aByteArray size * 2).
	aByteArray do: [ :aByte |
		| data |
		data := aByte printStringBase: 16 length: 2 padded: true.
		buffer nextPutAll: data
	].
	textualAddr := addr printStringBase: 16 length: 8 padded: true.
	textualSize := aByteArray size  printStringBase: 16.
	self writeBytesHex: buffer contents
		toAddrHex: textualAddr
		hexSize: textualSize!

----- Method: RemoteRAM>>writeBytesHex:toAddrHex:hexSize: (in category 'RSP protocol') -----
writeBytesHex: aString toAddrHex: addr hexSize: s
	| answer |
	answer := gdb
		q: 'M', addr,  ',', s, ':', aString.
	answer = 'OK' ifFalse: [ self error: 'RSP protocol failure' ].

!

----- Method: RemoteRAM>>writeInt32:toAddr: (in category 'writing') -----
writeInt32: int toAddr: addr
	"Use the REMOTE TARGET's endianness."
	self longAtAddr: addr put: int bigEndian: self isBigEndian!

----- Method: RemoteRAM>>writeInt32s:toAddr: (in category 'writing') -----
writeInt32s: arrayOfInt32s toAddr: addr
	"Using the remote endianness."
	self writeInt32s: arrayOfInt32s toAddr: addr bigEndian: self isBigEndian
!

----- Method: RemoteRAM>>writeInt32s:toAddr:bigEndian: (in category 'writing') -----
writeInt32s: arrayOfInt32s toAddr: addr bigEndian: bigEndian
	"Using the remote endianness."
	| buffer textualAddr textualSize |
	buffer := WriteStream on: ''.
	arrayOfInt32s do: [ :anInt32 |
		| data textualData |
		data := bigEndian
			ifTrue: [ anInt32 ]
			ifFalse: [ anInt32 byteSwap32 ].
		textualData := data printStringBase: 16 length: 8 padded: true.
		buffer nextPutAll: textualData
	].
	textualAddr := addr printStringBase: 16 length: 8 padded: true.
	textualSize := arrayOfInt32s size * 4 printStringBase: 16.
	self writeBytesHex: buffer contents
		toAddrHex: textualAddr
		hexSize: textualSize!

----- Method: SimulationAddressSpace class>>bytesPerElement (in category 'as yet unclassified') -----
bytesPerElement
	^1!

----- Method: SimulationAddressSpace class>>new: (in category 'as yet unclassified') -----
new: bytes
	"I really hate this design.
	The only reason #new: is here is because there is no concept of conneciton
	between the processor and memory."
	| instance |
	instance := self gdb: TargetAwareX86 current gdb.
	instance shmemSize: 120*1024*1024.
	^instance!

----- Method: SimulationAddressSpace>>currentInstructionEncoding (in category 'as yet unclassified') -----
currentInstructionEncoding
	^self readInt32fromAddr: self gdb pc!

----- Method: SimulationAddressSpace>>endianness (in category 'as yet unclassified') -----
endianness
	"Answer the endianness OF THE REMOTE TARGET."
	^self gdb processorDescription endian!

----- Method: SimulationAddressSpace>>isBigEndian (in category 'as yet unclassified') -----
isBigEndian
	^self endianness == #big!

Object subclass: #TAJTargetSetup
	instanceVariableNames: 'gdbClass host port architecture processor magicInstruction instructionStreamClass breakpointKind'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GDB-TAJ'!

----- Method: TAJTargetSetup class>>current (in category 'settings') -----
current
	^self gem5PPC   !

----- Method: TAJTargetSetup class>>gem5MIPS (in category 'settings') -----
gem5MIPS
	| arch |
	arch := self tajMIPSArchitecture.
	^self new
		gdbClass: Gem5SharedRAM8K;
		host: '192.168.75.2';
		port: 7000;
		architecture: arch;
		instructionStreamClass: TargetAgnosticInstructionStream;
		breakpointKind: #hard;
		magicInstruction: (((arch isa instructionAt: #sll) bind: (Dictionary new
			at: 'rs' put: 0;
			at: 'rt' put: 0;
			at: 'rd' put: 0;
			at: 'shamt' put: 0;
			yourself)) emit);
		yourself!

----- Method: TAJTargetSetup class>>gem5PPC (in category 'settings') -----
gem5PPC
	| arch |
	arch := self tajPOWERArchitecture.
	^self new
		gdbClass: Gem5SharedRAM;
		host: '127.0.0.1';
		port: 7000;
		architecture: arch;
		instructionStreamClass: TargetAgnosticInstructionStream;
		breakpointKind: #hard;
		magicInstruction: (((arch isa instructionAt: #ore) bind: (Dictionary new
			at: 'ra' put: 1;
			at: 'rb' put: 1;
			at: 'rs' put: 1;
			at: 'rc' put: 0;
			yourself)) emit);
		yourself!

----- Method: TAJTargetSetup class>>gem86 (in category 'settings') -----
gem86
	| arch |
	arch := self tajIA32Architecture.
	^self new
		gdbClass: BufferingRemoteRAM;
		host: '192.168.75.2';
		port: 7000;
		architecture: arch;
		instructionStreamClass: TargetAgnosticInstructionStream;
		
		"bogus, I still don't what it should be on x86"
		magicInstruction: 16r0;
		breakpointKind: #trap;
		yourself!

----- Method: TAJTargetSetup class>>mpc5125 (in category 'settings') -----
mpc5125
	| arch |
	arch := self tajPOWERArchitecture.
	^self new
		gdbClass: BufferingRemoteRAM;
		host: '192.168.75.39';
		port: 7000;
		architecture: arch;
		instructionStreamClass: TargetAgnosticInstructionStream;
		
		"twge r2,r2; see gdb/gdbserver/linux-ppc-low.c.
		ptrace will happily send SIGTRAP for any tw,
		but gdb's breakpoint_at() will be confused unless
		the memory contents are exactly what it thinks
		the SW break instruction is."
		magicInstruction: 16r7D821008;
		breakpointKind: #trap;
		yourself!

----- Method: TAJTargetSetup class>>p1025 (in category 'settings') -----
p1025
	"Freescale TWR-P1025 PowerPC e500v2"
	| arch |
	arch := self tajPOWERArchitecture.
	^self new
		gdbClass: BufferingRemoteRAM;
		host: '192.168.75.199';
		port: 7000;
		architecture: arch;
		instructionStreamClass: TargetAgnosticInstructionStream;
		
		"twge r2,r2; see gdb/gdbserver/linux-ppc-low.c.
		ptrace will happily send SIGTRAP for any tw,
		but gdb's breakpoint_at() will be confused unless
		the memory contents are exactly what it thinks
		the SW break instruction is."
		magicInstruction: 16r7D821008;
		breakpointKind: #trap;
		yourself!

----- Method: TAJTargetSetup class>>tajIA32Architecture (in category 'TAJArchitectures') -----
tajIA32Architecture
	^"TAJIA32Architecture"0 resetDefault!

----- Method: TAJTargetSetup class>>tajMIPSArchitecture (in category 'TAJArchitectures') -----
tajMIPSArchitecture
	^"TAJMIPSArchitecture"0 resetDefault!

----- Method: TAJTargetSetup class>>tajPOWERArchitecture (in category 'TAJArchitectures') -----
tajPOWERArchitecture
	^"TAJPowerArchitecture"0 resetDefault!

----- Method: TAJTargetSetup>>architecture (in category 'accessing') -----
architecture
	^ architecture!

----- Method: TAJTargetSetup>>architecture: (in category 'accessing') -----
architecture: anObject
	architecture := anObject!

----- Method: TAJTargetSetup>>breakpointKind (in category 'accessing') -----
breakpointKind
	^ breakpointKind!

----- Method: TAJTargetSetup>>breakpointKind: (in category 'accessing') -----
breakpointKind: anObject
	breakpointKind := anObject!

----- Method: TAJTargetSetup>>gdbClass (in category 'accessing') -----
gdbClass
	^ gdbClass!

----- Method: TAJTargetSetup>>gdbClass: (in category 'accessing') -----
gdbClass: anObject
	gdbClass := anObject!

----- Method: TAJTargetSetup>>host (in category 'accessing') -----
host
	^ host!

----- Method: TAJTargetSetup>>host: (in category 'accessing') -----
host: anObject
	host := anObject!

----- Method: TAJTargetSetup>>instructionStreamClass (in category 'accessing') -----
instructionStreamClass
	^ instructionStreamClass!

----- Method: TAJTargetSetup>>instructionStreamClass: (in category 'accessing') -----
instructionStreamClass: anObject
	instructionStreamClass := anObject!

----- Method: TAJTargetSetup>>magicInstruction (in category 'accessing') -----
magicInstruction
	^ magicInstruction!

----- Method: TAJTargetSetup>>magicInstruction: (in category 'accessing') -----
magicInstruction: anObject
	magicInstruction := anObject!

----- Method: TAJTargetSetup>>port (in category 'accessing') -----
port
	^ port!

----- Method: TAJTargetSetup>>port: (in category 'accessing') -----
port: anObject
	port := anObject!

Object subclass: #TargetAwareX86
	instanceVariableNames: 'gdb'
	classVariableNames: 'Current ExtendedOpcodeExceptionMap OpcodeExceptionMap PostBuildStackDelta'
	poolDictionaries: ''
	category: 'GDB-Cog'!

----- Method: TargetAwareX86 class>>current (in category 'instance creation') -----
current
	^Current!

----- Method: TargetAwareX86 class>>initialize (in category 'class initialization') -----
initialize
	"TargetAwareX86 initialize"
	| it |
	it := self basicNew.
	PostBuildStackDelta := 0.
	OpcodeExceptionMap := Array new: 256 withAll: #handleExecutionPrimitiveFailureAt:in:.
	OpcodeExceptionMap
		at: 1 + it callOpcode			put: #handleCallFailureAt:in:;
		at: 1 + it jmpOpcode			put: #handleJmpFailureAt:in:;
		at: 1 + it retOpcode			put: #handleRetFailureAt:in:;
		at: 1 + it movALObOpcode	put: #handleMovALObFailureAt:in:;
		at: 1 + it movAXOvOpcode	put: #handleMovAXOvFailureAt:in:;
		at: 1 + it movObALOpcode	put: #handleMovObALFailureAt:in:;
		at: 1 + it movOvAXOpcode	put: #handleMovOvAXFailureAt:in:;
		at: 1 + it movGvEvOpcode	put: #handleMovGvEvFailureAt:in:;
		at: 1 + it movEvGvOpcode	put: #handleMovEvGvFailureAt:in:;
		at: 1 + it movGbEbOpcode	put: #handleMovGbEbFailureAt:in:;
		at: 1 + it movEbGbOpcode	put: #handleMovEbGbFailureAt:in:.
	ExtendedOpcodeExceptionMap := Array new: 256 withAll: #handleExecutionPrimitiveFailureAt:in:.
	ExtendedOpcodeExceptionMap
		at: 1 + it movGvEbOpcode put: #handleMovGvEbFailureAt:in:!

----- Method: TargetAwareX86 class>>new (in category 'instance creation') -----
new
	"
	TargetAwareX86 new
	"
	Current := super new connectGdb.
	^Current!

----- Method: TargetAwareX86>>cResultRegister (in category 'accessing-abstract') -----
cResultRegister
	^self eax!

----- Method: TargetAwareX86>>callOpcode (in category 'opcodes') -----
callOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA8"
	^16rE8!

----- Method: TargetAwareX86>>connectGdb (in category 'target connection') -----
connectGdb
	gdb := self debuggerClass
		host: self hostIP
		port: self tcpPort
		processorDescription: self pdl.
	^self "not gdb; #new needs the instance"!

----- Method: TargetAwareX86>>debuggerClass (in category 'target connection') -----
debuggerClass
	^RemoteGDBSession!

----- Method: TargetAwareX86>>eax (in category 'intel registers') -----
eax
	^gdb getRegister: 'eax'!

----- Method: TargetAwareX86>>eax: (in category 'intel registers') -----
eax: anUnsignedInteger
	gdb setRegister: 'eax' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>ebp (in category 'intel registers') -----
ebp
	^gdb getRegister: 'ebp'!

----- Method: TargetAwareX86>>ebp: (in category 'intel registers') -----
ebp: anUnsignedInteger
	gdb setRegister: 'ebp' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>ebx (in category 'intel registers') -----
ebx
	^gdb getRegister: 'ebx'!

----- Method: TargetAwareX86>>ebx: (in category 'intel registers') -----
ebx: anUnsignedInteger
	gdb setRegister: 'ebx' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>ecx (in category 'intel registers') -----
ecx
	^gdb getRegister: 'ecx'!

----- Method: TargetAwareX86>>ecx: (in category 'intel registers') -----
ecx: anUnsignedInteger
	gdb setRegister: 'ecx' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>edi (in category 'intel registers') -----
edi
	^gdb getRegister: 'edi'!

----- Method: TargetAwareX86>>edi: (in category 'intel registers') -----
edi: anUnsignedInteger
	gdb setRegister: 'edi' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>edx (in category 'intel registers') -----
edx
	^gdb getRegister: 'edx'!

----- Method: TargetAwareX86>>edx: (in category 'intel registers') -----
edx: anUnsignedInteger
	gdb setRegister: 'edx' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>eip (in category 'intel registers') -----
eip
	^gdb getRegister: 'eip'!

----- Method: TargetAwareX86>>eip: (in category 'intel registers') -----
eip: anUnsignedInteger
	gdb setRegister: 'eip' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>esi (in category 'intel registers') -----
esi
	^gdb getRegister: 'esi'!

----- Method: TargetAwareX86>>esi: (in category 'intel registers') -----
esi: anUnsignedInteger
	gdb setRegister: 'esi' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>esp (in category 'intel registers') -----
esp
	^gdb getRegister: 'esp'!

----- Method: TargetAwareX86>>esp: (in category 'intel registers') -----
esp: anUnsignedInteger
	gdb setRegister: 'esp' to: anUnsignedInteger.
	^anUnsignedInteger!

----- Method: TargetAwareX86>>fp (in category 'accessing-abstract') -----
fp
	^self ebp!

----- Method: TargetAwareX86>>gdb (in category 'target connection') -----
gdb
	^gdb!

----- Method: TargetAwareX86>>handleCallFailureAt:in: (in category 'error handling') -----
handleCallFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a call into a ProcessorSimulationTrap signal."
	|  relativeJump |
	relativeJump := memoryArray longAt: pc + 2 bigEndian: false.
	"NB: CRAP!! CRAP!! CRAP!!  The real CPU already pushed the return address!!"
	self esp: self esp + 4.
	
	^(ProcessorSimulationTrap
			pc: pc
			nextpc: pc + 5
			address: (pc + 5 + relativeJump) signedIntToLong
			type: #call)
		signal!

----- Method: TargetAwareX86>>handleExecutionPrimitiveFailureAt:in: (in category 'error handling') -----
handleExecutionPrimitiveFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Handle an execution primitive failure for an unhandled opcode."
	^self reportPrimitiveFailure!

----- Method: TargetAwareX86>>handleExecutionPrimitiveFailureIn:minimumAddress: (in category 'execution') -----
handleExecutionPrimitiveFailureIn: memoryArray minimumAddress: minimumAddress
"NB: THIS SHOULD GO INTO A COMMON SemihostABI CLASS -- bgs"
	"Handle an execution primitive failure.  Convert out-of-range call and absolute
	 memory read into register instructions into ProcessorSimulationTrap signals."
	"self printIntegerRegistersOn: Transcript"
	"self printRegistersOn: Transcript"
	| pc opcode |
	pc := self eip.
	"	(() between: minimumAddress and: memoryArray byteSize - 1) ifTrue: self  reportPrimitiveFailure -- TODO"
	opcode := memoryArray byteAt: pc + 1.
	opcode ~= 16r0f ifTrue:
			[^self
				perform: (OpcodeExceptionMap at: opcode + 1)
				with: pc
				with: memoryArray].
	opcode := memoryArray byteAt: pc + 2.
	^self
				perform: (ExtendedOpcodeExceptionMap at: opcode + 1)
				with: pc
				with: memoryArray!

----- Method: TargetAwareX86>>handleJmpFailureAt:in: (in category 'error handling') -----
handleJmpFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a jmp into a ProcessorSimulationTrap signal."
	|  relativeJump |
	relativeJump := memoryArray longAt: pc + 2 bigEndian: false.
	^(ProcessorSimulationTrap
			pc: pc
			nextpc: pc + 5
			address: (pc + 5 + relativeJump) signedIntToLong
			type: #jump)
		signal!

----- Method: TargetAwareX86>>handleMovALObFailureAt:in: (in category 'error handling') -----
handleMovALObFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a read into al into a ProcessorSimulationTrap signal."
	^(ProcessorSimulationTrap
			pc: pc
			nextpc: pc + 5
			address: (memoryArray unsignedLongAt: pc + 2 bigEndian: false)
			type: #read
			accessor: #al:)
		signal!

----- Method: TargetAwareX86>>handleMovAXOvFailureAt:in: (in category 'error handling') -----
handleMovAXOvFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a read into eax into a ProcessorSimulationTrap signal."
	^(ProcessorSimulationTrap
			pc: pc
			nextpc: pc + 5
			address: (memoryArray unsignedLongAt: pc + 2 bigEndian: false)
			type: #read
			accessor: #eax:)
		signal!

----- Method: TargetAwareX86>>handleMovEbGbFailureAt:in: (in category 'error handling') -----
handleMovEbGbFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a byte register write into a ProcessorSimulationTrap signal."
	| modrmByte address |
	modrmByte := memoryArray byteAt: pc + 2.
	(modrmByte bitAnd: 7) ~= 4 ifTrue: "MoveRMbr with r = ESP requires an SIB byte"
		[address := (modrmByte bitAnd: 16rC0)
					caseOf: {
						[0 "ModRegInd"]
						->	[memoryArray unsignedLongAt: pc + 3 bigEndian: false].
						[16r80 "ModRegRegDisp32"]
						->	[(self perform: (#(eax ecx edx ebx esp ebp esi edi) at: (modrmByte bitAnd: 7) + 1))
								+ (memoryArray unsignedLongAt: pc + 3 bigEndian: false)
								bitAnd: 16rFFFFFFFF] }
					otherwise: [^self reportPrimitiveFailure].
		^(ProcessorSimulationTrap
				pc: pc
				nextpc: pc + 6
				address: address
				type: #write
				accessor: (#(al cl dl bl ah ch dh bh) at: ((modrmByte >> 3 bitAnd: 7) + 1)))
			signal].
	^self reportPrimitiveFailure!

----- Method: TargetAwareX86>>handleMovEvGvFailureAt:in: (in category 'error handling') -----
handleMovEvGvFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a register write into a ProcessorSimulationTrap signal."
	| modrmByte |
	^((modrmByte := memoryArray byteAt: pc + 2) bitAnd: 16rC7) = 16r5 "ModRegInd & disp32"
		ifTrue:
			[(ProcessorSimulationTrap
					pc: pc
					nextpc: pc + 6
					address: (memoryArray unsignedLongAt: pc + 3 bigEndian: false)
					type: #write
					accessor: (#(eax ecx edx ebx esp ebp esi edi) at: ((modrmByte >> 3 bitAnd: 7) + 1)))
				signal]
		ifFalse:
			[self reportPrimitiveFailure]!

----- Method: TargetAwareX86>>handleMovGbEbFailureAt:in: (in category 'error handling') -----
handleMovGbEbFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a byte register load into a ProcessorSimulationTrap signal."
	| modrmByte address |
	modrmByte := memoryArray byteAt: pc + 2.
	address := (modrmByte bitAnd: 16rC0)
					caseOf: {
						[0 "ModRegInd"]
						->	[memoryArray unsignedLongAt: pc + 3 bigEndian: false].
						[16r80 "ModRegRegDisp32"]
						->	[(self perform: (#(eax ecx edx ebx esp ebp esi edi) at: (modrmByte bitAnd: 7) + 1))
								+ (memoryArray unsignedLongAt: pc + 3 bigEndian: false)
								bitAnd: 16rFFFFFFFF] }
					otherwise: [^self reportPrimitiveFailure].
	^(ProcessorSimulationTrap
			pc: pc
			nextpc: pc + 6
			address: address
			type: #read
			accessor: (#(al: cl: dl: bl: ah: ch: dh: bh:) at: ((modrmByte >> 3 bitAnd: 7) + 1)))
		signal!

----- Method: TargetAwareX86>>handleMovGvEbFailureAt:in: (in category 'error handling') -----
handleMovGvEbFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a register load into a ProcessorSimulationTrap signal."
	| modrmByte mode srcIsSP srcVal dst offset |
	modrmByte := memoryArray byteAt: pc + 3.
	mode := modrmByte >> 6 bitAnd: 3.
	dst := #(eax: ecx: edx: ebx: esp: ebp: esi: edi:) at: ((modrmByte >> 3 bitAnd: 7) + 1).
	mode = 0 ifTrue: "ModRegInd"
		[offset := memoryArray unsignedLongAt: pc + 4. "1-relative"
		 ^(ProcessorSimulationTrap
					pc: pc
					nextpc: pc + 7
					address: offset
					type: #read
					accessor: dst)
				signal].
	srcIsSP := (modrmByte bitAnd: 7) = 4.
	srcVal := self perform: (#(eax ecx edx ebx esp ebp esi edi) at: (modrmByte bitAnd: 7) + 1).
	mode = 1 ifTrue: "ModRegRegDisp8"
		[offset := memoryArray byteAt: pc + (srcIsSP ifTrue: [5] ifFalse: [4]). "1-relative"
		 offset > 127 ifTrue: [offset := offset - 256].
		 ^(ProcessorSimulationTrap
					pc: pc
					nextpc: pc + (srcIsSP ifTrue: [5] ifFalse: [4])
					address: (srcVal + offset bitAnd: 16rFFFFFFFF)
					type: #read
					accessor: dst)
				signal].
	mode = 2 ifTrue: "ModRegRegDisp32"
		[offset := memoryArray unsignedLongAt: pc + (srcIsSP ifTrue: [5] ifFalse: [4]). "1-relative"
		 ^(ProcessorSimulationTrap
					pc: pc
					nextpc: pc + (srcIsSP ifTrue: [8] ifFalse: [7])
					address: (srcVal + offset bitAnd: 16rFFFFFFFF)
					type: #read
					accessor: dst)
				signal].
	^self reportPrimitiveFailure!

----- Method: TargetAwareX86>>handleMovGvEvFailureAt:in: (in category 'error handling') -----
handleMovGvEvFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a register load into a ProcessorSimulationTrap signal."
	| modrmByte |
	^(((modrmByte := memoryArray byteAt: pc + 2) bitAnd: 16rC7) = 16r5) "ModRegInd & disp32"
		ifTrue:
			[(ProcessorSimulationTrap
					pc: pc
					nextpc: pc + 6
					address: (memoryArray unsignedLongAt: pc + 3 bigEndian: false)
					type: #read
					accessor: (#(eax: ecx: edx: ebx: esp: ebp: esi: edi:) at: ((modrmByte >> 3 bitAnd: 7) + 1)))
				signal]
		ifFalse:
			[self reportPrimitiveFailure]!

----- Method: TargetAwareX86>>handleMovObALFailureAt:in: (in category 'error handling') -----
handleMovObALFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a byte write of al into a ProcessorSimulationTrap signal."
	^(ProcessorSimulationTrap
			pc: pc
			nextpc: pc + 5
			address: (memoryArray unsignedLongAt: pc + 2 bigEndian: false)
			type: #write
			accessor: #al)
		signal!

----- Method: TargetAwareX86>>handleMovOvAXFailureAt:in: (in category 'error handling') -----
handleMovOvAXFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a write of eax into a ProcessorSimulationTrap signal."
	^(ProcessorSimulationTrap
			pc: pc
			nextpc: pc + 5
			address: (memoryArray unsignedLongAt: pc + 2 bigEndian: false)
			type: #write
			accessor: #eax)
		signal!

----- Method: TargetAwareX86>>handleRetFailureAt:in: (in category 'error handling') -----
handleRetFailureAt: pc "<Integer>" in: memoryArray "<Bitmap|ByteArray>"
	"Convert an execution primitive failure for a ret into a ProcessorSimulationTrap signal."
	^(ProcessorSimulationTrap
			pc: pc
			nextpc: pc + 1
			address: (memoryArray unsignedLongAt: self esp + 1)
			type: #return
			accessor: #eip:)
		signal!

----- Method: TargetAwareX86>>hostIP (in category 'target connection') -----
hostIP
	^'192.168.75.2'!

----- Method: TargetAwareX86>>integerRegisterState (in category 'accessing-abstract') -----
integerRegisterState
	| registerState |
	registerState := gdb getRegisters.
	^{ 'eax'.  'ebx'.  'ecx'.  'edx'.  'esp'.  'ebp'.  'esi'.  'edi'. 'eip'.  'eflags'} collect: [ :aRegName |
		registerState at: aRegName ]
!

----- Method: TargetAwareX86>>jmpOpcode (in category 'opcodes') -----
jmpOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA8"
	^16rE9!

----- Method: TargetAwareX86>>lockPrefix (in category 'opcodes') -----
lockPrefix
	^16rF0!

----- Method: TargetAwareX86>>movALObOpcode (in category 'opcodes') -----
movALObOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA7"
	^16rA0!

----- Method: TargetAwareX86>>movAXOvOpcode (in category 'opcodes') -----
movAXOvOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA7"
	^16rA1!

----- Method: TargetAwareX86>>movEbGbOpcode (in category 'opcodes') -----
movEbGbOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2"
	^16r88!

----- Method: TargetAwareX86>>movEvGvOpcode (in category 'opcodes') -----
movEvGvOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA8"
	^16r89!

----- Method: TargetAwareX86>>movGbEbOpcode (in category 'opcodes') -----
movGbEbOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2"
	^16r8A!

----- Method: TargetAwareX86>>movGvEbOpcode (in category 'opcodes') -----
movGvEbOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A3, pA14"
	^16rB6!

----- Method: TargetAwareX86>>movGvEvOpcode (in category 'opcodes') -----
movGvEvOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA8"
	^16r8B!

----- Method: TargetAwareX86>>movObALOpcode (in category 'opcodes') -----
movObALOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA7"
	^16rA2!

----- Method: TargetAwareX86>>movOvAXOpcode (in category 'opcodes') -----
movOvAXOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA7"
	^16rA3!

----- Method: TargetAwareX86>>nopOpcode (in category 'opcodes') -----
nopOpcode
	^16r90!

----- Method: TargetAwareX86>>pc (in category 'accessing-abstract') -----
pc
	^self eip!

----- Method: TargetAwareX86>>pc: (in category 'accessing-abstract') -----
pc: newPC
	^self eip: newPC!

----- Method: TargetAwareX86>>pdl (in category 'target connection') -----
pdl
	^FakeProcessorDescriptionX86 new!

----- Method: TargetAwareX86>>printOn: (in category 'printing') -----
printOn: aStream
	self gdb printRegistersOn: aStream
!

----- Method: TargetAwareX86>>pushWord:in: (in category 'cog') -----
pushWord: aValue in: aMemory
	| sp |
	sp := (self esp: self esp - 4).
	aMemory longAt: sp + 1 put: aValue bigEndian: false!

----- Method: TargetAwareX86>>remoteMemoryClass (in category 'target connection') -----
remoteMemoryClass
	^Gem5SharedRAM!

----- Method: TargetAwareX86>>retOpcode (in category 'opcodes') -----
retOpcode
	"[1] IA-32 Intel® Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z.
		table A2, pA7"
	^16rC3!

----- Method: TargetAwareX86>>runInMemory:minimumAddress:readOnlyBelow: (in category 'execution') -----
runInMemory: aMemory minimumAddress: minimumAddress readOnlyBelow: minimumWritableAddress
	| stopReason |
	stopReason := gdb c.
	stopReason signal = #SIGSEGV ifFalse: [ self shouldBeImplemented ].
	^self
		handleExecutionPrimitiveFailureIn: aMemory
		minimumAddress: minimumAddress!

----- Method: TargetAwareX86>>setFramePointer:stackPointer: (in category 'accessing-abstract') -----
setFramePointer: framePointer stackPointer: stackPointer
	"Initialize the processor's frame and stack pointers"
	self ebp: framePointer.
	self esp: stackPointer!

----- Method: TargetAwareX86>>simulateLeafCallOf:nextpc:memory: (in category 'cog') -----
simulateLeafCallOf: address nextpc: nextpc memory: aMemory
"this should go back to the alien"
	self pushWord: nextpc in: aMemory.
	self eip: address!

----- Method: TargetAwareX86>>singleStepIn:minimumAddress:readOnlyBelow: (in category 'execution') -----
singleStepIn: aMemory minimumAddress: minimumAddress readOnlyBelow: minimumWritableAddress
	| stopReason |
	stopReason := gdb s.
	stopReason signal = #SIGTRAP ifTrue: [ ^self "no fault" ].
	^self
		handleExecutionPrimitiveFailureIn: aMemory
		minimumAddress: minimumAddress!

----- Method: TargetAwareX86>>smashRegisterAccessors (in category 'accessing-abstract') -----
smashRegisterAccessors
	^#(eax: ebx: ecx: edx: esi: edi:)!

----- Method: TargetAwareX86>>smashRegistersWithValuesFrom:by: (in category 'accessing-abstract') -----
smashRegistersWithValuesFrom: base by: step
	self smashRegisterAccessors
	   withIndexDo:
		[:accessor :index|
		self perform: accessor with: index - 1 * step + base]!

----- Method: TargetAwareX86>>sp (in category 'accessing-abstract') -----
sp
	^self esp!

----- Method: TargetAwareX86>>sp: (in category 'accessing-abstract') -----
sp: anAddress
	"Set whatever the processor considers its stack pointer to anAddress."
	self esp: anAddress!

----- Method: TargetAwareX86>>tcpPort (in category 'target connection') -----
tcpPort
	^7000!

----- Method: TargetAwareX86>>topOfStackIn: (in category 'printing') -----
topOfStackIn: aMemory
	"The 32-bit word at the stack top"
	^aMemory unsignedLongAtAddr: self esp bigEndian: false!




More information about the Vm-dev mailing list