Index | wersja polska |
Executing machine code programs happens to be possible due to holes in the INPUT command implementation.
Traditionally, the first piece of code written in a new environment or language is one that prints "Hello world". This one will not be an exception, either:
.radix 16 ;all numbers hexadecimal start: mov #41,@#8264 ;system variable loop: jsr r4,print1 .asciz "HELLO WORLD! " br loop print1: jsr pc,@#1248 ;print string (ROM call) rts r4
Unfortunately, there's a limited choice of codes which can be typed on the keyboard, and the program should to some degree look like BASIC to be accepted by the editor. The modified code below fulfills these requirements:
start: 8272: bmi 82B6 8296: jsr r4,@#82B0 829A: .asciz "HELLO=WORLD!=" 82A8: jsr r5,@#82AE 82AC: sob r0,8296 82AE: rts r5 82B0: jsr pc,@#1248 82B4: rts r4 82B6: mov #8041,@#8264 82BC: sob r0,8296
Finally, here's the code which has to be typed in the program area P0 (because only the first one has fixed starting address):
Regular Latin characters are coloured black, Cyrillic ones are red, graphic ones are green, BASIC keywords are blue. Care should be given to type them correctly, because different characters from different sets can look very similar.
8260: 7B 81 00 00 81 00 00 00 00 08 00 0A 00 DF 38 32 {.............82 8270: 35 36 21 81 30 00 14 00 E7 00 1E 00 21 30 31 32 56!.0.......!012 8280: 33 34 35 36 37 38 39 00 28 00 21 30 31 32 33 34 3456789.(.!01234 8290: 35 36 37 38 39 00 1F 09 B0 82 48 45 4C 4C 4F 3D 56789.....HELLO= 82A0: 57 4F 52 4C 44 21 3D 00 5F 09 AE 82 0C 7E 85 00 WORLD!=._....... 82B0: DF 09 48 12 84 00 DF 15 41 80 64 82 14 7E 00 00 ..H.....A.d.....
mk85fipr.zip - RAM image for the emulator
Command INPUT treats the numeric literal in the argument field like a string variable $, which is caused by missing check what is actually returned by the expression evaluator (at about address 1C62). The string of up to 1E characters typed at the INPUT prompt is copied to the RAM address allocated to the variable. The address of the variable is taken from the second word of the stack entry. In case of a numeric literal this word contains first four digits of the mantissa.
The method exploits an indirect jump via the variable 8258, found in the ROM at address 0E76. This part of the code can be, among others, reached from the direct mode main loop at about address 0BC6, originally designed to resume the BASIC program execution after a STOP condition.
The variable 8258 is misused by the INPUT procedure as temporary storage for the pointer to the next interpreted BASIC statement. Therefore, the data following the INPUT statement could be executed as machine code program if we manage to pass control to the direct mode main loop, while meeting following requirements:
In this situation pressing [EXE] is incorrectly interpreted as a request to resume the BASIC program execution, which effectively starts the user's machine code pointed to by variable 8258.
Currently there are two methods known to me to enter the direct mode main loop:
Usually the STOP condition is caused either by pressing the [STOP] key, or by the STOP instruction, or in the TRACE mode. It's handled by the routine 179A, which displays the program and line number, then goes to the direct mode.
A false STOP condition can be forced by setting bit 0 of the mode variable 8256 by changing the contents of this variable from 8000 to 8001 (by typing the string in response to the
INPUT 8256
statement in the line 10).
Character code 01 cannot be typed from the keyboard, but code 31 (character '1') fits our purposes too.
Typing '1' alone would also change the most significant byte of the mode variable to 00, because the typed string is terminated by zero.
So, it is necessary to enter three more characters to preserve the next system variable 8258 as well.
The terminating byte 00 will affect the variable 825A, which is unimportant in this context.
To enter the direct mode, some BASIC command valid both in program and direct modes has to be executed, for example VAC (in line 20). These commands have a common exit point at address 1652, where the program flow continues to the direct mode main loop 0BA0, or the BASIC interpreter main loop, depending on the state of the bit 0 of the mode variable.
The [AC] key is handled at address 05C0 in the ROM. This procedure clears the input buffer and the display, then enters the direct mode main loop without altering the variables 8256 and 8258. When using this method, the INPUT argument doesn't matter (it doesn't need to be a numeric literal, any variable would do), neither does the VAC command in the line 20 (as it never gets executed).
Writing machine code distinguished as BASIC appears to be a cumbersome and time consuming task. The procedure described below allows assembly programming without any artificial limitations.
This step creates a safe place for machine code programs by changing the ramtop system variable 8252.
The space from the ramtop up to physical end of the RAM will remain unaccessible to the BASIC interpreter.
The ramtop variable can be modified with following program:
10 INPUT 8252:VAC
Writing 8680 to the ramtop variable will give a space of 0180 bytes starting at address 8680 (all numbers hexadecimal). This value is equivalent to a string typed at the INPUT prompt. The program needs to be run only once. The new memory configuration will last until the init switch on the back of the calculator is pressed, or the TEST command is executed.
Next program will copy a binary file (stored as a list of hex numerals in BASIC lines) to the destination area, then perform an indirect jump to the beginning of the code. As in the previous example, some weirdness is caused by the need to squeeze it to the imposed BASIC program layout.
.radix 16 .loc 8272 start: bmi start1 .loc 8286 exec: jmp (r5) loop: tstb (r2)+ ;separator or end-of-line character nop bne byte nop jsr r0,@#return ;dummy line: tstb (r2)+ cmpb (r2)+,#2727 ;test if line number > 9987 bcc exec1 tstb (r2)+ ;skip the '!' nop jsr r0,return ;dummy byte: jsr pc,@#digit nop jsr pc,digit inc r4 ;dummy movb r0,(r3)+ sob r1,loop exec1: sob r2,exec start1: bmi start2 return: rts r0 digit: asl r0 asl r0 asl r0 asl r0 nop mov r0,-(sp) movb (r2)+,r0 cmpb r0,#4141 ;'A' bcs digit1 sub #3737,r0 add (sp)+,r0 rts pc start2: mov #4080,r1 ;arbitrary chosen value add #8800-4080,r1 ;r1 = top of RAM nop mov #4080,r2 add #data+1-4080,r2 ;r2 = source address nop mov 8252,r3 ;destination address = ramtop mov r3,r5 ;jump vector sub r3,r1 ;max. number of bytes sob r2,line digit1: sub #3030,r0 add (sp)+,r0 rts pc data: ;data start here
The loader program should be followed by a sequence of BASIC lines containing a list of bytes in hexadecimal format, starting with an exclamation mark, and separated by a single character (comma or space preferred). Any number of bytes in a line is allowed. The code should be terminated by a line with a number 9988 or greater. The example below was assembled from the initial version of the "Hello world" program:
9000 !DF,15,41,00,64,82,37,09 9010 !10,00,48,45,4C,4C,4F,20 9020 !57,4F,52,4C,44,21,20,00 9030 !F6,01,DF,09,48,12,84,00 9999 END
It's a lot of typing!
Everything should be checked twice before proceeding!
So looks the loader along with the data lines in the RAM:
8260: 6D 81 00 80 00 80 00 00 00 00 00 0A 00 DF 38 32 m.............82 8270: 35 36 21 81 30 00 14 00 E7 00 1E 00 21 30 31 32 56!.0.......!012 8280: 33 34 35 36 37 00 4D 00 D2 8B A0 00 0B 02 A0 00 34567.M......... 8290: 1F 08 B8 82 D2 8B 97 A4 27 27 0C 86 D2 8B A0 00 ........''...... 82A0: 37 08 14 00 DF 09 BA 82 A0 00 F7 09 0C 00 84 0A 7............... 82B0: 13 90 56 7E 98 7E 0F 81 80 00 C0 0C C0 0C C0 0C ..V............. 82C0: C0 0C A0 00 26 10 80 94 17 A0 41 41 13 87 C0 E5 ....&.....AA.... 82D0: 37 37 80 65 87 00 C1 15 80 40 C1 65 80 47 A0 00 77.e.....@.e.G.. 82E0: C2 15 80 40 C2 65 7D 42 A0 00 C3 17 52 82 C5 10 ...@.e}B....R... 82F0: C1 E0 B0 7E C0 E5 30 30 80 65 87 00 28 23 21 44 ......00.e..(#!D 8300: 46 2C 31 35 2C 34 31 2C 30 30 2C 36 34 2C 38 32 F,15,41,00,64,82 8310: 2C 33 37 2C 30 39 00 32 23 21 31 30 2C 30 30 2C ,37,09.2#!10,00, 8320: 34 38 2C 34 35 2C 34 43 2C 34 43 2C 34 46 2C 32 48,45,4C,4C,4F,2 8330: 30 00 3C 23 21 35 37 2C 34 46 2C 35 32 2C 34 43 0.<#!57,4F,52,4C 8340: 2C 34 34 2C 32 31 2C 32 30 2C 30 30 00 46 23 21 ,44,21,20,00.F#! 8350: 46 36 2C 30 31 2C 44 46 2C 30 39 2C 34 38 2C 31 F6,01,DF,09,48,1 8360: 32 2C 38 34 2C 30 30 00 0F 27 E4 00 00 00 00 00 2,84,00..'......
mk85load.zip - RAM image for the emulator
The program should be started the same way as the previous "Hello world" example.
Once the machine code is in place, the loader program can be deleted. The machine code will stay in the memory until the init switch on the back of the calculator is pressed, or the TEST command is executed.
This short program executes the loaded machine code in absence of the loader:
82B6: mov @#8252,r0 82BA: jmp (r0)
8260: 6D 81 00 80 00 80 00 00 00 00 00 0A 00 DF 38 32 m.............82 8270: 35 36 21 81 30 00 14 00 E7 00 1E 00 21 30 31 32 56!.0.......!012 8280: 33 34 35 36 37 38 39 00 28 00 21 30 31 32 33 34 3456789.(.!01234 8290: 35 36 37 38 39 00 32 00 21 30 31 32 33 34 35 36 56789.2.!0123456 82A0: 37 38 39 00 3C 00 21 30 31 32 33 34 35 36 37 38 789.<.!012345678 82B0: 39 30 31 32 33 00 C0 17 52 82 48 00 00 00 00 00 90123...R.H.....
mk85strt.zip - RAM image for the emulator