Since that part is described as "self modifying code" the conclusion is, thatthat not allows this modification.
the self-modification doesn't go exactly as well in different circumstances. That conclusion is correct. The bug is not in mina, but in the msdos emulator
Since that part is described as "self modifying code" the conclusion is, thatThat conclusion is correct. The bug is not in mina, but in the msdos emulator
the self-modification doesn't go exactly as well in different circumstances.
that not allows this modification.
Well maybe it cannot be called „a bug” in strict sense, still it is non-reliable code:
„For Intel486(TM) processors, a write to an instruction in the cache will modify
it in both the cache and memory, but if the instruction was prefetched before the write, the old version of the instruction could be the one executed.”
( https://www.jaist.ac.jp/iscenter-new/mpc/altix/altixdata/opt/intel/vtune/doc/users_guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/pentium4_hh/events4/self-modifying_code_clear.htm )
So it's confirmed 100%. Creating self-modifying code for the processors higher
than 386 seems to be risky business, unfortunately.
Since that part is described as "self modifying code" the conclusion is, thatThat conclusion is correct. The bug is not in mina, but in the msdos emulator
the self-modification doesn't go exactly as well in different circumstances.
that not allows this modification.
Well maybe it cannot be called „a bug” in strict sense, still it is non-reliable code:
„For Intel486(TM) processors, a write to an instruction in the cache will modify
it in both the cache and memory, but if the instruction was prefetched before
the write, the old version of the instruction could be the one executed.”
( https://www.jaist.ac.jp/iscenter-new/mpc/altix/altixdata/opt/intel/vtune/doc/users_guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/pentium4_hh/events4/self-modifying_code_clear.htm )
So it's confirmed 100%. Creating self-modifying code for the processors higherBut what is the official position? All I could find was:
than 386 seems to be risky business, unfortunately.
IA-32 Intel® Architecture Software Developer’s Manual Volume 3:
On 25/07/2022 07:07, Zbig wrote:
So it's confirmed 100%. Creating self-modifying code for the processors higher
than 386 seems to be risky business, unfortunately.
IA-32 Intel® Architecture Software Developer’s Manual Volume 3:
To write self-modifying code and ensure that it is compliant with current
and future versions of the IA-32 architecture, one of the following two
coding options must be chosen:
(* OPTION 1 *)
Store modified code (as data) into code segment;
Jump to new code or an intermediate location;
Execute new code;
(* OPTION 2 *)
Store modified code (as data) into code segment;
Execute a serializing instruction; (* For example, CPUID instruction *)
Execute new code;
(The use of one of these options is not required for programs intended
to run on the Pentium or Intel486 processors, but are recommended to
insure compatibility with the Pentium 4, Intel Xeon, and P6 family
processors.)
Risky? You just follow the rules, and it works.So it's confirmed 100%. Creating self-modifying code for the processors higher
than 386 seems to be risky business, unfortunately.
Risky? You just follow the rules, and it works.So it's confirmed 100%. Creating self-modifying code for the processors higher
than 386 seems to be risky business, unfortunately.
Oh, I don't know. Most of the time it works — but then one finds
the case when it doesn't, and then there are controversies: „no,
it's not the code; it's bad emulator” or the like.
From what I see the technique is better to be avoided, unless it is
„really really” needed for some particular reason.
...
IA-32 Intel® Architecture Software Developer’s Manual Volume 3:
To write self-modifying code and ensure that it is compliant with current >> and future versions of the IA-32 architecture, one of the following two
coding options must be chosen:
(* OPTION 1 *)
Store modified code (as data) into code segment;
Jump to new code or an intermediate location;
Execute new code;
That's the rule I stated above.
(* OPTION 2 *)
Store modified code (as data) into code segment;
Execute a serializing instruction; (* For example, CPUID instruction *)
Execute new code;
Note that CPUID only exists on the Pentium and later CPUs. I wonder
what that rule is about? Who would use a slow serializing instruction (typically 10+ cycles) instead of a fast jump and make their program
less portable at the same time?
(The use of one of these options is not required for programs intended
to run on the Pentium or Intel486 processors, but are recommended to
insure compatibility with the Pentium 4, Intel Xeon, and P6 family
processors.)
I am absolutely certain that the jump rule is needed for the 486 and
earlier CPUs. I don't know if it's needed for the more recent CPUs
mentioned above.
What steps did you take to rule out MINA as the cause of your error?
Does it happen with DX-Forth? I used self-modifying code which you
can test with:
s" foobar$" drop 'DX ! 9 doscall
From what I see the technique is better to be avoided, unless it is „really really” needed for some particular reason.Such as when there's no single 'INT' instruction.
[...]
As DX-Forth uses the same scheme (self-modify an 'INT 0') it should also
fail assuming that's the cause. To rule out MINA, I would patch INT 00h to >> INT 66h (unused interrupt). AFAIK 'Runtime error 200 at' is a Borland code >> and message.
So I did it, changing "INT 00" to "INT 66". While with "INT 00" it spits out "Divide error" under debug (but then I'm able to continue), in case of "INT 66"
the machine is hung up.
Of course DXForth works as usual (no problems).
From the above it certainly looks like MINA isn't modifying the 'INT 00' instr
- but why if the DX-Forth DOSCALL test I posted works? I'll compare the codes
to see if I can spot something. It doesn't make sense that SMC should work on
DX-Forth but not on MINA when run on the same machine/setup.
OK, so I made the test you've posted and yes, it works like this:
s" foobar$" drop 'DX ! 9 doscall foobar ok
: test begin $30 doscall key? until key drop ;
It proves nothing if it works but a fail would be interesting.
You are using FreeDOS - can you boot a genuine MS-DOS?
: test begin $30 doscall key? until key drop ;
It proves nothing if it works but a fail would be interesting.
It seems to be working OK. Waited some time and ended the
loop with keypress.
You are using FreeDOS - can you boot a genuine MS-DOS?
Not on that particular 486 — too much hassle with such „transition”. But honestly: reliable code should work on either one, not „only for
MS-DOS (tm)". FreeDOS isn't an emulator — and I already wrote, that
exactly under emulator (which uses FreeDOS files, BTW) Mina — rather surprisingly, in such situation — works correctly?
On 26/07/2022 19:26, Anton Ertl wrote:
...
IA-32 Intel® Architecture Software Developer’s Manual Volume 3:
To write self-modifying code and ensure that it is compliant with current >>> and future versions of the IA-32 architecture, one of the following two >>> coding options must be chosen:
(* OPTION 1 *)
Store modified code (as data) into code segment;
Jump to new code or an intermediate location;
Execute new code;
That's the rule I stated above.
(* OPTION 2 *)
Store modified code (as data) into code segment;
Execute a serializing instruction; (* For example, CPUID instruction *) >>> Execute new code;
Note that CPUID only exists on the Pentium and later CPUs. I wonder
what that rule is about? Who would use a slow serializing instruction
(typically 10+ cycles) instead of a fast jump and make their program
less portable at the same time?
(The use of one of these options is not required for programs intended
to run on the Pentium or Intel486 processors, but are recommended to
insure compatibility with the Pentium 4, Intel Xeon, and P6 family
processors.)
I am absolutely certain that the jump rule is needed for the 486 and
earlier CPUs. I don't know if it's needed for the more recent CPUs
mentioned above.
I've no experience but assuming so I would at least expect to see it
stated in the docs for the 486. At the time the 486 was created, DOS programs were still widely used and for Intel to release a CPU that
was incompatible would be 'A courageous decision, Minister'.
If you are able re-assemble MINA, try inserting a JMP per Intel:
MOV BYTE [RQBIOS+1],AL ; Patch the code.
JMP XXX
XXX: POP DX
p.s. Change that so the interval between modify and execute exceeds 32 bytes.
p.s. Change that so the interval between modify and execute exceeds 32 bytes.
Indeed after insertion of 32 NOPs — which along with following few instructions
gave a little more than 32 bytes — it started to work. But when I was trying to fix
that with two shorter jumps „back and forth” — no way.
I'll simply replace that SMC with „ordinary” code.
Indeed after insertion of 32 NOPs — which along with following few instructionsGoogling I got the impression a JMP to the next instruction should have worked.
gave a little more than 32 bytes — it started to work. But when I was trying to fix
that with two shorter jumps „back and forth” — no way.
Did you use a 3-byte JMP instr? As both BIOSO and BIOSN self-modify, both need
patching.
If the JMP's don't work it would be possible to split the routines and satisfy
the 32-byte distance requirement without the need for padding.
I'll simply replace that SMC with „ordinary” code.It rather defeats the purpose. I wouldn't do it to DX-Forth. A programmer is expected to solve problems - not run away from them :)
Indeed after insertion of 32 NOPs — which along with following few instructionsGoogling I got the impression a JMP to the next instruction should have worked.
gave a little more than 32 bytes — it started to work. But when I was trying to fix
that with two shorter jumps „back and forth” — no way.
Did you use a 3-byte JMP instr? As both BIOSO and BIOSN self-modify, both need
patching.
Indeed NASM optimized these jumps to 2-byte instruction — so I changed it to be
both 'JMP LONG' explicitly. Unfortunately, it didn't change much; mina breaks as
before.
If the JMP's don't work it would be possible to split the routines and satisfy
the 32-byte distance requirement without the need for padding.
Probably. Even very likely.
I'll simply replace that SMC with „ordinary” code.It rather defeats the purpose. I wouldn't do it to DX-Forth. A programmer is >> expected to solve problems - not run away from them :)
Actually I'm not sure is it worth the effort.
From what I see the most important is „DOS dispatcher” INT 21h, and just a few more, like: INT 10, 11, 12, 13, 15, 16, 19, 1A, 20, 33 (h). That makes eleven interrupts together. So for such handful it's possible to handle the problem
using kind of table, being 100% sure nothing will break in case the program will be
run on another, even different processor, that maybe will behave some slighthly different
way. If there was need to use, say, 60 different interrupts or so — well, that would be
quite different story. But if there's no need — maybe it would be practical to follow the
advice from „Thinking Forth”, I mean: „Generality usually involves complexity. Don't
generalize your solution any more than will be required; instead, keep it changeable”.
I'll simply replace that SMC with „ordinary” code.
As for SMC failing on later CPUs I would have expected
to see reports of same. I ran MINA on a Pentium without issue. Without evidence
to the contrary I would treat 486 as a special case. No point crippling software
because Intel gave birth to one bastard. Similarly the idea SMC is the devil's
work to be avoided at any cost.
So yes, maybe „one bastard” — still it's the quite ubiquitous one (if we mean that now
„retro” gear). Really a pity I don't have any AMD 486 for comparison.
On the other hand: it's good to know, that unmodified Mina (or just that short procedure)
can be used for testing CPUs regarding potential SMC (un)reliability.
MOV BYTE [RQBIOS+1],AL ; Patch the code.
JMP XXX
XXX: POP DX
Otherwise any backwards JMP should do it.
Otherwise any backwards JMP should do it.
Two days ago I changed that code following way:
POP AX ; Function code
; Once we are more acknowledgeable, put segment overwrite here.
JMP XXX1 ; senseless jump to make self-modifying code work on 486 YYY1: MOV BYTE [RQBIOS+1],AL ; Patch the code.
POP DX
POP CX
POP BX
POP AX
PUSH SI ; Save Forth registers. NEEDED?
PUSH BP
; XCHG SI,AX ; Save AX in (already free) SI
; XCHG SI,AX
RQBIOS: INT(0) ; Request number to be overwritten.
PUSHF ; Save status into DI
POP DI
; XCHG SI,AX ; Save AX in (still free) SI
; XCHG SI,AX
POP BP ; Restore Forth registers. NEEDED?
POP SI
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DI ; i.e. flags
JMP SHORT ZZZ1
XXX1: JMP YYY1 ; senseless return
ZZZ1: JMP NEXT
; SELF MODIFYING CODE ENDS HERE! YOU HAVE BEEN WARNED!
So, as you can see there are even two jumps — forth and back — and this wasn't of any help.
That's why I became somewhat cautious with SMC.
JMP XXX1 ; senseless jump to make self-modifying code work on 486
[..]
XXX1: JMP YYY1 ; senseless return
Are you saying the following patch (applied to both BIOSO and BIOSN) doesn't work?
MOV BYTE [RQBIOS+1],AL ; Patch the code.
JMP XXX
NOP
XXX: POP DX
Are you saying the following patch (applied to both BIOSO and BIOSN) doesn't >> work?
MOV BYTE [RQBIOS+1],AL ; Patch the code.
JMP XXX
NOP
XXX: POP DX
Yep, that works. Thanks.
Great! Can you confirm:
1) Changing JMP XXX to JMP SHORT XXX it still works
2) Deleting the NOP causes it to fail
Great! Can you confirm:
[...]
2) Deleting the NOP causes it to fail
When I commented-out the NOPs, it... still works.
Hi,
the actual raspberry os does no longer support wiringPi, which was a way to access GPIO under GForth as described here: https://forums.raspberrypi.com/viewtopic.php?t=207597
Is there now another (easy) way?
Thanks in advance! Christof
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 512 |
Nodes: | 16 (2 / 14) |
Uptime: | 90:53:03 |
Calls: | 10,018 |
Calls today: | 1 |
Files: | 13,849 |
D/L today: |
1 files (9K bytes) |
Messages: | 6,365,856 |