Jump to content

Search the Community

Showing results for tags 'pascal'.

More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


  • Atari Systems
    • Atari General
    • Atari 2600
    • Atari 5200
    • Atari 7800
    • Atari Lynx
    • Atari Jaguar
    • Atari VCS
    • Dedicated Systems
    • Atari 8-Bit Computers
    • Atari ST/TT/Falcon Computers
  • Classic Consoles
  • Classic Computing
  • Modern Consoles
  • Gaming General
  • Marketplace
  • Community
  • Community
  • Game Programming
  • Site
  • PC Gaming
  • The Club of Clubs's Discussion
  • I Hate Sauron's Topics
  • 1088 XEL/XLD Owners and Builders's Topics
  • Atari BBS Gurus's Community Chat
  • Atari BBS Gurus's BBS Callers
  • Atari BBS Gurus's BBS SysOps
  • Atari BBS Gurus's Resources
  • Atari Lynx Programmer Club's CC65
  • Atari Lynx Programmer Club's ASM
  • Atari Lynx Programmer Club's Lynx Programming
  • Atari Lynx Programmer Club's Music/Sound
  • Atari Lynx Programmer Club's Graphics
  • The Official AtariAge Shitpost Club's Shitty meme repository
  • The Official AtariAge Shitpost Club's Read this before you enter too deep
  • Arcade Gaming's Discussion
  • Tesla's Vehicles
  • Tesla's Solar
  • Tesla's PowerWall
  • Tesla's General
  • Harmony/Melody's CDFJ
  • Harmony/Melody's DPC+
  • Harmony/Melody's BUS
  • Harmony/Melody's General
  • ZeroPage Homebrew's Discussion
  • Furry Club's Chat/RP
  • PSPMinis.com's General PSP Minis Discussion and Questions
  • PSPMinis.com's Reviews
  • Atari Lynx 30th Birthday's 30th Birthday Programming Competition Games
  • 3D Printing Club's Chat
  • Drivers' Club's Members' Vehicles
  • Drivers' Club's Drives & Events
  • Drivers' Club's Wrenching
  • Drivers' Club's Found in the Wild
  • Drivers' Club's General Discussion
  • Dirtarians's General Discussion
  • Dirtarians's Members' Rigs
  • Dirtarians's Trail Runs & Reports
  • Dirtarians's Wrenching
  • The Green Herb's Discussions
  • Robin Gravel's new blog's My blog
  • Robin Gravel's new blog's Games released
  • Atari Video Club's Harmony Games
  • Atari Video Club's The Atari Gamer
  • Atari Video Club's Video Game Summit
  • Atari Video Club's Discsuuions
  • Star Wars - The Original Trilogy's Star Wars Talk
  • PlusCart User's Bug reports
  • PlusCart User's Discussion
  • DMGD Club's Incoming!
  • DASM's General
  • AtariVox's Topics
  • Gran Turismo's Gran Turismo
  • Gran Turismo's Misc.
  • Gran Turismo's Announcements
  • The Food Club's Food
  • The Food Club's Drinks
  • The Food Club's Read me first!
  • The (Not So) Official Arcade Archives Club's Rules (READ FIRST)
  • The (Not So) Official Arcade Archives Club's Feedback
  • The (Not So) Official Arcade Archives Club's Rumor Mill
  • The (Not So) Official Arcade Archives Club's Coming Soon
  • The (Not So) Official Arcade Archives Club's General Talk
  • The (Not So) Official Arcade Archives Club's High Score Arena
  • Adelaide South Australia Atari Chat's General Chat & Welcome
  • Adelaide South Australia Atari Chat's Meets
  • Adelaide South Australia Atari Chat's Trades & Swaps
  • KC-ACE Reboot's KC-ACE Reboot Forum
  • The Official Lost Gaming Club's Lost Gaming
  • The Official Lost Gaming Club's Undumped Games
  • The Official Lost Gaming Club's Tip Of My Tounge
  • The Official Lost Gaming Club's Lost Gaming Vault
  • The Official Lost Gaming Club's Club Info
  • GIMP Users's Discussion
  • The Homebrew Discussion's Topics


There are no results to display.

There are no results to display.


  • AtariAge Calendar
  • The Club of Clubs's Events
  • Atari BBS Gurus's Calendar

Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start










Custom Status



Currently Playing

Playing Next

Found 9 results

  1. Update: moved packages to emulation section from software. I have put a together MAME/MESS emulation package that emulates the MyArc Extended BASIC II & TI P-Card for the Ti99. MyarcXBII: MyArc was a company that made peripherals for the TI-99 line. There most famous peripheral (if you want to call it that) was the Geneve. The Geneve was a complete TMS 9995 computer that fit in the TI-99 PEB as a card. Another peripheral that was lesser known, but just as revolutionary, was the MyArc Extended BASIC II. The MyXBII consisted of the 128k or 512k Memory card, a set of disk and a cartridge. When running the MYXBII had 3 times the memory of TIXB, was up to twice as fast as TIXB and was able to access all the graphic capability of the TI graphics card including the hi-rez. If a MyArc HD disk card was added the software could even boot from the MyArc hard drive. It turned the TI99 into a real power house. Unfortunately because of it's expense and the fact that you needed a PEB to run it, the MyArcXBII never really caught on. Now, though, with emulation it cost nothing so through the power of MAME/MESS the power is being released. Start the MyArcXBII at the TI99 main menu by choosing 3-128k BASIC and this will boot the MyArcXBII from the hard drive. P-Card: The P-Card was a card for the PEB that was, more or less, a complete operating system apart from the TI99. It's a virtual machine processor on a card that ran P-Code. It was written totally in software and was based on Pascal and was able to run on other computers that also conformed to the P-Code specifications. It is nothing like a standard TI-99 and when booted takes over the TI-99 and even has a specially formatted disk it uses. There is a complete suite of software and if you can figure it out, kinda nice. To run the P-Card in MAME/MESS click under OPTIONS-DIP SWITCHES then turn on the P-CARD. Hard reboot the machine and the TI-99 will start in the P-Card mode (after a few seconds of beeping and blank pages). To go back to MyArcXBII just turn the P-CARd switch OFF then hard reset the machine. The MAME/MESS package works with any versions of MAME/MESS past version 222. Just merge your version of MAME/MESS into the MESSxxx directory and point the already created batch file in the root to that directory. Package includes manuals, software, batch files and everything you need except MAME/MESS itself. Enjoy. Download from my https://ti99resources.wordpress.com/emulation/ At the bottom of the page is MAME packages, click on MyArc Extended BASIC II to show download files. I have both a package with and without the P-Card. (a truly nice tripped out Ti-99 from mainbyte.com)
  2. I finally got Turbo Pasc99 running. I created a small program to print “Hello World”. It compiles fine so I compiled It to DSK1.TEST. So how do I convert this to a program and run it? I would prefer to create program files that can be used with just TIPI, I think that is an EA5 file? When I load “test” into the editor, I can see what looks like assembly language to me. Do I use the Assembler next?
  3. In the last couple of posts we explored some of the APX Pascal architecture, showing bits of disassembly of the runtime, but I neglected to include the tools I used to extract those bits. This post aims to remedy that, and produce a first draft listing of the APX Pascal runtime. The runtime disassembly was done using similar perl code as I developed in the Dealer Demo Forth deconstruction blog posts last year, so refer to those if you want a discussion on how this disassembler works. There's a few differences worth discussing though. I've split the code into pieces, m6502.pm being generic support for disassembly, pascal.pm being Pascal p-code decompilation and pascal.pl handling working with the PASCAL runtime object from Side 1 of the APX Pascal disk. If you use a perl 5.26 or later you may need to set PERL_USE_UNSAFE_INC=1 in your environment, or add the path with the code to your perl INC path. read_img has been implemented using read_img_core which takes a description of an image. The Dealer Demo version was just a one-off custom piece of code. For apxpascal.pl, the definition is: read_img_core( $addr, $size, '../pascal.xex', [0x06 - 0x3300, 0x3300, 0x59ff], [0x06 + 0x3a00 - 0x3300 - 0xa000, 0xa000, 0xbfff]); which means if you ask for an address between 0x3300 and 0x59ff, start at offset 0x6 of the runtime file. If you ask for an address between 0xa000 and 0xbfff, start at offset 0x706 of the runtime file. The core of the subroutines bytes and words have been factored into bytes_buf and words_buf to allow the code to be used in more cases. Some new options have been added to the core: -str for displaying a string with a known size, -scr for displaying a CR-delimited string. The option -mads, for translating a listing into a MADS compatible source file, is much more sophisticated now. It does some validation of the listing, and converts the listing bytes into a 'check.obx' file. The code is largely the same as the code I've used before when validating OCR's of source code listings (e.g. Atari PILOT or Star Raiders). The code from $A000 to $BFFF is the runtime and is pure assembly code. I produced it by initially disassembling all the code in that region, and then stitching in corrections where data appeared in the stream (usually by noticing a BRK or .BYTE slip in to the disassembly). The version 0.0 documentation (https://archive.org/details/AtariPascalV0.0Documentation) was then used to help pick some label names for common memory locations. I decided to use IP for the pseudo-PC. The commonly location $CC I suspected matched the TMPBASE label from the documentation. $CA was more-or-less exclusively used for saving and restoring the X-register, so XSAVE seemed a natural label (echoes of the Forth VM there). Finally I concluded DR0 must be $B6 (one of 8 16-bit display registers) and LCLBASE (local base register) $C6. $0600 is clearly used as an evaluation stack, so I used EVALPAGE from the 0.0 documentation. I haven't conclusively identified PRGSP, EVALSP, and LEXLEVEL, nor the many temporaries, and the previous labels could prove erroneous, but this is enough for now to produce a more readable listing. The code from $3300 to $39FF is a combination of assembly code and p-code. Whenever a JSR $1F06 appears, some number of JMPs before it appear, followed by what appears to be a length and then a sequence of p-code. A single appearance of JSR $1F03 appears to be followed by an address and then a sequence of p-code. So I decompile decode starting 2 bytes after a JSR $1F03 (until seeing opcode $D9) or $1F06 (until seeing opcode $A6). This description is almost certainly incorrect in some fashion, but seems adequate for now to start decompiling the image. You can find the pascal p-code decompiler in pascal.pm. The decompiler is very simple, it contains a list of p-code instruction lengths. Those that are non-zero are decompiled, the rest are assumed to be errors (not all the values are yet filled in). Negative lengths dump the additional data as words instead of bytes, and opcode $2C is handled as a special case. $2C is clearly a code for a string if you look at examples in the file (e.g. address $3421 contains 0x2c,5,'D:MON'), so clearly a fixed length decompiler isn't going to work for that case. Using this we obtain snippets of p-code like so: 3410: 4C 13 34 JMP *+3 3413: 20 03 1F JSR $1F03 3416: 3A 34 .WORD $343A 3418: A2 .BYTE $A2 3419: 21 35 .WORD $3521 341B: FD 09 .BYTE $FD,$09 341D: 25 .BYTE $25 341E: 3A 34 .WORD $343A 3420: F1 .BYTE $F1 3421: 2C .BYTE $2C 3422: 05 44 3A .BYTE 5,'D:MON' 3425: 4D 4F 4E 3428: A2 .BYTE $A2 3429: E0 36 .WORD $36E0 342B: 25 .BYTE $25 342C: 3A 34 .WORD $343A 342E: F1 .BYTE $F1 342F: A2 .BYTE $A2 3430: 7D 35 .WORD $357D 3432: 25 .BYTE $25 3433: 3A 34 .WORD $343A 3435: F1 .BYTE $F1 3436: A2 .BYTE $A2 3437: 64 35 .WORD $3564 3439: D9 .BYTE $D9 and so: 36DD: 4C 00 00 JMP $0000 36E0: 4C E3 36 JMP *+3 36E3: 20 06 1F JSR $1F06 36E6: 56 00 .WORD $56 36E8: A8 00 .BYTE $A8,$00 36EA: D2 .BYTE $D2 36EB: BC 51 .BYTE $BC,$51 36ED: 09 52 .BYTE $09,$52 36EF: 09 54 .BYTE $09,$54 36F1: 01 54 .BYTE $01,$54 36F3: A8 00 .BYTE $A8,$00 36F5: BC 10 .BYTE $BC,$10 36F7: A6 .BYTE $A6 We need to assign identifiers to those opcodes (first bytes) to make this more readable, but we can do that later as we better identify the opcodes. The current decompilation and the tools are attached to this post. In my next post we'll continue examining the opcodes and improving the listing. pascal1.zip
  4. I had the following post on planet-99.net - can anyone offer any advice...
  5. Bill Lange has been blogging about Atari Pascal since early February at https://insideataripascal.blogspot.com/, so here's my own small contribution after spending an afternoon poking around in APX Pascal and looking for the core interpreter. If we look at the PASCAL runtime on the APX Pascal disk, it's a simple enough image. It loads itself from disk to $3300-$59ff and then starts running from $3300. So what does that initial bootstrap code do? Here's the preamble: 3300: A2 00 LDX #0 3302: A9 0C LDA #$0C 3304: 9D 42 03 STA ICCMD,X 3307: 20 56 E4 JSR CIOV 330A: AD A7 33 LDA $33A7 330D: 85 F0 STA $F0 330F: AD A8 33 LDA $33A7+1 3312: 85 F1 STA $F0+1 3314: AD A9 33 LDA $33A9 3317: 85 F2 STA $F2 3319: AD AA 33 LDA $33A9+1 331C: 85 F3 STA $F2+1 331E: AD AB 33 LDA $33AB 3321: 85 F4 STA $F4 3323: AD AC 33 LDA $33AB+1 3326: 85 F5 STA $F4+1 3328: 20 73 33 JSR $3373 This code closes IOCB #0, then sets up $F0-F5 using values at $33A7-$33AC and then calls a subroutine. Those values are: 33A7: 00 3A .WORD $3A00 ; source 33A9: 00 A0 .WORD $A000 ; destination 33AB: 00 20 .WORD $2000 ; count 33AD: 00 1D .WORD $1D00 And the subroutine looks like: 3373: A0 00 LDY #0 3375: B1 F0 LDA ($F0),Y 3377: 91 F2 STA ($F2),Y 3379: A5 F0 LDA $F0 337B: 18 CLC 337C: 69 01 ADC #1 337E: 85 F0 STA $F0 3380: A5 F1 LDA $F0+1 3382: 69 00 ADC #0 3384: 85 F1 STA $F0+1 3386: A5 F2 LDA $F2 3388: 18 CLC 3389: 69 01 ADC #1 338B: 85 F2 STA $F2 338D: A5 F3 LDA $F2+1 338F: 69 00 ADC #0 3391: 85 F3 STA $F2+1 3393: A5 F4 LDA $F4 3395: 38 SEC 3396: E9 01 SBC #1 3398: 85 F4 STA $F4 339A: A5 F5 LDA $F4+1 339C: E9 00 SBC #0 339E: 85 F5 STA $F4+1 33A0: A5 F4 LDA $F4 33A2: 05 F5 ORA $F4+1 33A4: D0 CF BNE $3375 33A6: 60 RTS This is just a block copy routine, which relocates all the code at $3A00-$59FF to $A000-$BFFF. This block of code (which is most of the PASCAL executable), is the actual runtime. A simpler way to do this would have been to use a multi-segment load file, but this works well enough. $A000-$BFFF is the cartridge address space for an 8k cart, so clearly this was intended at one point to be shipped as a cartridge. What happens next: 332B: AD AD 33 LDA $33AD 332E: 85 80 STA $80 3330: AD AE 33 LDA $33AE 3333: 85 81 STA $81 3335: A9 00 LDA #0 3337: 85 82 STA $82 3339: 85 83 STA $83 333B: 20 00 A2 JSR $A200 ... A200: 4C 12 B8 JMP $B812 This copies the word in $33AD ($1D00) to $80,$81 and zeros $82,$83 before invoking a routine at $A200, which vectors to $B812. That routine does several things, including: B834: A5 80 LDA $80 B836: 85 D0 STA $D0 B838: A5 81 LDA $81 B83A: 85 D1 STA $D1 B83C: AD 78 A2 LDA $A278 B83F: 85 CE STA $CE B841: AD 79 A2 LDA $A278+1 B844: 85 CF STA $CE+1 B846: AD 7A A2 LDA $A27A B849: 85 D2 STA $D2 B84B: AD 7B A2 LDA $A27A+1 B84E: 85 D3 STA $D2+1 B850: 20 6B AE JSR $AE6B where: A278: 00 A0 .WORD $A000 A27A: 78 02 .WORD $0278 So we move the word at $80,$81 ($1D00) to $D0,D1, and set $CE,$CF to $A000 and $D2,D3 to $0278, before calling another block copier. AE6B: A0 00 LDY #0 AE6D: A6 D3 LDX $D3 AE6F: F0 0E BEQ $AE7F AE71: B1 CE LDA ($CE),Y AE73: 91 D0 STA ($D0),Y AE75: C8 INY AE76: D0 F9 BNE $AE71 AE78: E6 CF INC $CF AE7A: E6 D1 INC $D1 AE7C: CA DEX AE7D: D0 F2 BNE $AE71 AE7F: A6 D2 LDX $D2 AE81: F0 08 BEQ $AE8B AE83: B1 CE LDA ($CE),Y AE85: 91 D0 STA ($D0),Y AE87: C8 INY AE88: CA DEX AE89: D0 F8 BNE $AE83 AE8B: 60 RTS So we relocate the first $0278 bytes of the "cartridge" to address $1D00-$1F77. The first $200 bytes are just a series of addresses (more on those soon), the next $78 bytes are a set of JMP vectors, e.g. 1F00: 4C 12 B8 JMP $B812 1F03: 4C B1 AB JMP $ABB1 1F06: 4C B6 AB JMP $ABB6 ... 1F72: 4C 87 B8 JMP $B887 1F75: 4C 5F BC JMP $BC5F After we return from this the code continues with: B853: A5 80 LDA $80 B855: 85 82 STA $82 B857: A5 81 LDA $81 B859: 85 83 STA $83 B85B: E6 83 INC $83 B85D: E6 83 INC $83 B85F: 20 EB B9 JSR $B9EB The word at $80,$81 gets moved to $82,$83 and incremented by $200, so the word at $82,$83 is now $1F00. The subroutine called looks like: B9EB: A2 21 LDX #$21 B9ED: A0 00 LDY #0 B9EF: B9 CA B9 LDA $B9CA,Y B9F2: 99 92 00 STA $0092,Y B9F5: C8 INY B9F6: CA DEX B9F7: D0 F6 BNE $B9EF B9F9: A5 81 LDA $81 B9FB: 85 AD STA $AD B9FD: 85 B2 STA $B2 B9FF: E6 B2 INC $B2 BA01: 60 RTS This copies the code at $B9CA into page zero, and patches the value at $81 ($1D) into $AD and the $B2 and then increments $B2, so we end up with the following: 0092: 18 CLC 0093: 65 A4 ADC $A4 0095: 85 A4 STA $A4 0097: 90 0A BCC $00A3 0099: E6 A5 INC $A5 009B: B0 06 BCS $00A3 009D: E6 A4 INC $A4 009F: D0 02 BNE $00A3 00A1: E6 A5 INC $A5 00A3: AD FF FF LDA $FFFF 00A6: 0A ASL A 00A7: B0 05 BCS $00AE 00A9: 85 AC STA $AC 00AB: 6C 00 1D JMP ($1D00) 00AE: 85 B1 STA $B1 00B0: 6C 00 1E JMP ($1E00) This is the core of the Pascal interpreter, similar to the Forth NEXT routine I discussed in http://atariage.com/forums/blog/734/entry-15007-dealer-demo-part-4-some-forth-at-last/. It has three parts, and self-modifies its code as it runs. If you enter at $0092, it increments the current p-code pointer (located at $A4,$A5) by the accumulator. If you enter at $009D, it increments the current p-code pointer by 1. In both cases, it then proceeds to the third part (which can be called directly as well) which reads the p-code value, multiplies the value by two and then patches one of two jump vectors with that value depending on whether the multiply overflowed or not. This allows us to dispatch all 256 possible p-codes, and each code will then jump back into this routine, keeping the interpreter running forever. Of course, we haven't actually gotten into the interpreter yet, only set it up. We'll discuss that in a future post, but we've made decent progress towards separating the runtime from the monitor. In particular, it's clear we could move the 8K runtime in $A000-$BFFF into a cartridge image and modify the PASCAL file to skip the initial relocation and rely on the cartridge. That focuses our attention on the remaining 1.8K of PASCAL to isolate the code that sets up the runtime and loads the MON program. Hopefully we can adapt that code to load another program directly, and thus produce binaries that can run without loading the monitor.
  6. Created a little package for MAME/MESS that enables MyArcXBII and P-Card in one pre-made package. Just add latest MAME/MESS and go. It's on my project page that accesses my https://ti99resources.wordpress.com/:
  7. I have been testing the waters of Atari APX Pascal recent and have been having an issue with MEDIT blowing up when run from the Pascal menu. I twittered the great Bill Lange who gave me the fix. (1) As you probably know, you have to copy the MEDIT executable over to your work disk image in disk drive D2. In addition, MEDIT needs a minor modification for it to work with the Atari Pascal Language System. (2) With DOS loaded, put the work disk into D1. Use the DOS menu option N to create a MEM.SAV file on the work disk. (3) Using the DOS menu option L, BINARY LOAD MEDIT using the /N (MEDIT/N) option to prevent it from running. This will write to the MEM.SAV file on the work disk. (4) Using the DOS menu option K, BINARY SAVE it back as follows: MEDIT/A, 2600, 2601. This append operation tells the Pascal program point to begin execution at the MEDIT entry point. (5) Put the work disk back in D2. Put APLS Disk #1 in D1. You should now be able to launch MEDIT from the Atari Pascal Monitor. the twitter conversation: https://twitter.com/BillLange1968/status/1087211748921434113 Thanks, Bill Lange
  8. The last blog entry introduced the tools I'm using to explore the Pascal runtime, and included a preliminary (i.e. rough) disassembly. Now we'll start refining that disassembly and start discussing more of the opcodes. Firstly, the last listing was erroneous around $B959 to $B991. There are strings there I somehow missed when spot checking the disassembly, so I've fixed up that part of the disassembly. There were also a couple of missing $9B's as well after strings, and the p-code disassembly had a couple of errors as well which I've now fixed. Now let's discuss some more opcodes. The simplest opcode in the listing is opcode DB. It is just: AF9D: E8 INX AF9E: E8 INX AF9F: 4C 9D 00 JMP NEXT_OP1 Since X is the current evaluation stack pointer, and it grows downwards, this opcode drops the topmost entry of the stack, so let's call it DROP. Another simple opcode is $DA, which disassembles as: AF8C: CA DEX AF8D: CA DEX AF8E: BD 03 06 LDA EVALPAGE+3,X AF91: 9D 01 06 STA EVALPAGE+1,X AF94: BD 02 06 LDA EVALPAGE+2,X AF97: 9D 00 06 STA EVALPAGE,X AF9A: 4C 9D 00 JMP NEXT_OP1 This adds one entry to the stack, and copies the (previous) top element to it, so we can call this DUP. Opcode D2 is a bit longer, but just involves moving things around the stack, so that the first two elements are exchanged, so let's call it SWAP. AF5F: BC 00 06 LDY EVALPAGE,X AF62: BD 02 06 LDA EVALPAGE+2,X AF65: 9D 00 06 STA EVALPAGE,X AF68: 98 TYA AF69: 9D 02 06 STA EVALPAGE+2,X AF6C: BC 01 06 LDY EVALPAGE+1,X AF6F: BD 03 06 LDA EVALPAGE+3,X AF72: 9D 01 06 STA EVALPAGE+1,X AF75: 98 TYA AF76: 9D 03 06 STA EVALPAGE+3,X AF79: 4C 9D 00 JMP NEXT_OP1 Some other simple stack-only opcodes are 30 (AND), 32 (OR), 34 (NOT), 36 (EOR), 38 (NEG), 40 (ADD) and 44 (SUB). All of these replace the top two values on the stack with the result of the operation. Opcodes 60 and 70 oddly point to the same code, which looks like this: B185: BD 01 06 LDA EVALPAGE+1,X B188: DD 03 06 CMP EVALPAGE+3,X B18B: D0 5C BNE $B1E9 B18D: BD 00 06 LDA EVALPAGE,X B190: DD 02 06 CMP EVALPAGE+2,X B193: D0 54 BNE $B1E9 B195: F0 5F BEQ $B1F6 ... B1E9: E8 INX B1EA: E8 INX B1EB: A9 00 LDA #0 B1ED: 9D 00 06 STA EVALPAGE,X B1F0: 9D 01 06 STA EVALPAGE+1,X B1F3: 4C 9D 00 JMP NEXT_OP1 B1F6: E8 INX B1F7: E8 INX B1F8: A9 01 LDA #1 B1FA: 9D 00 06 STA EVALPAGE,X B1FD: A9 00 LDA #0 B1FF: 9D 01 06 STA EVALPAGE+1,X B202: 4C 9D 00 JMP NEXT_OP1 If the top two values are equal, we replace them with a 1, otherwise we replace them with a 0. So let's call them EQU. Opcodes 62 and 72 reverses this, so let's call them NEQ. Now why are there two equivalent opcodes? Well, let's look at opcode 64 and 74. 64 is simply: B1A9: 20 2F BE JSR $BE2F B1AC: F0 3B BEQ $B1E9 B1AE: 30 39 BMI $B1E9 B1B0: 10 44 BPL $B1F6 and 74 is similar: B1C9: 20 2F BE JSR $BE2F B1CC: F0 1B BEQ $B1E9 B1CE: 90 19 BCC $B1E9 B1D0: B0 24 BCS $B1F6 with BE2F: BD 02 06 LDA EVALPAGE+2,X BE32: DD 00 06 CMP EVALPAGE,X BE35: F0 0B BEQ $BE42 BE37: BD 03 06 LDA EVALPAGE+3,X BE3A: FD 01 06 SBC EVALPAGE+1,X BE3D: 09 01 ORA #1 BE3F: 70 0A BVS $BE4B BE41: 60 RTS BE42: BD 03 06 LDA EVALPAGE+3,X BE45: FD 01 06 SBC EVALPAGE+1,X BE48: 70 01 BVS $BE4B BE4A: 60 RTS BE4B: 49 80 EOR #$80 BE4D: 09 01 ORA #1 BE4F: 60 RTS This difference here seems to be whether the 16-bit comparisons here are done signed or unsigned. The 6x opcodes are signed comparisons, and the 7x opcodes are unsigned comparisons. 60 is EQU and 70 is UEQU, which happen to have identical implementations, and 62 and 72 are similarly NEQ and UNEQ. 64, 66, 68 and 6A seem to be greater than (GT), less than (LT), greater than or equal (GTE) and less than or equal (LTE) respectively. 74, 76, 78 and 7A appear to be same, only unsigned. To further complicate matters, the 8x opcodes also implement comparisons (the same six EQU, NEQ, GT, LT, GTE, LTE operations), but for other types than signed and unsigned integers. The second byte after determines the type, with 00 => bool, 01 => string (both from the stack, so both of these sequences consume 2 bytes), and 02, 03 and 04 being various byte comparisons consuming an additional 2 bytes after the type byte. So our simple p-code disassembler which assumes all opcodes but 2C are fixed size needs to be modified to handle these opcodes a little differently. That's enough for this post. The runtime disassembly is certainly starting to make a bit more sense, but there are plenty of mysteries left to explore. pascal3.zip
  9. In the last post, we worked through layers of the APX Pascal runtime to find the main interpreter loop, which in fact resides entirely in page zero. In this post, we're going to dig into some of the opcodes to get a flavor for the runtime implementation. As we discussed last time, the each opcode is represented by a JMP value in a 512-byte table that is copied into $1D00 when the runtime starts. If you peruse though the table, the most common JMP target is $B9B5, in 81 entries. This is the not-implemented opcode, hitting any of these in code would be an error. The code seems to be an infinite loop. B9B5: 38 SEC B9B6: A0 00 LDY #0 B9B8: A9 67 LDA #$67 B9BA: 20 EA B8 JSR $B8EA B9BD: 4C B5 B9 JMP $B9B5 The next most common opcode is a 4-way tie, for opcodes $90-$97 ($AA65), $98-9F ($AA7A), $E0-E7 ($A2F5) and $E8-EF ($A8EC). Let's investigate each in turn. AA65: 4A LSR A AA66: 29 07 AND #7 AA68: 48 PHA AA69: A0 01 LDY #1 AA6B: B1 A4 LDA (IP),Y AA6D: 18 CLC AA6E: 65 C8 ADC $C8 AA70: 85 A4 STA IP AA72: 68 PLA AA73: 65 C9 ADC $C8+1 AA75: 85 A5 STA IP+1 AA77: 4C A3 00 JMP $00A3 This appears to be some kind of unconditional branch/jump opcode. The opcode is shifted right and masked, yielding a number 0-3. It then adds this to the value in $C8,C9 plus the value following the opcode. We set the IP to this value and continue execution. AA7A: A8 TAY AA7B: BD 00 06 LDA $0600,X AA7E: E8 INX AA7F: E8 INX AA80: 4A LSR A AA81: B0 03 BCS $AA86 AA83: 98 TYA AA84: 90 DF BCC $AA65 AA86: A9 02 LDA #2 AA88: 4C 92 00 JMP $0092 This pulls the top of data stack (the stack is at $0600 and indexed by X), if it's odd we're done, otherwise we call the branch function above. So it's a conditional branch. Both of these codes are very odd. The only reason I can think of to encode part of the branch offset into the opcode is to extend the range beyond 256 bytes, but in that case, why not just have a separate opcode for long branches. Also, using both BCC and BCS isn't optimal. A little thought shows removing the BCS achieves the same result, but faster. In general the runtime code looks like it could have used a little more optimization. This project started out as a port from the 8080, perhaps the author never developed enough 6502 experience to tighten up the code in the time allowed. The next two routines are similar: A2F5: 29 0F AND #$0F A2F7: A8 TAY A2F8: B1 C6 LDA ($C6),Y A2FA: C8 INY A2FB: CA DEX A2FC: CA DEX A2FD: 9D 00 06 STA $0600,X A300: B1 C6 LDA ($C6),Y A302: 9D 01 06 STA $0601,X A305: 4C 9D 00 JMP $009D and: A8EC: 29 0F AND #$0F A8EE: A8 TAY A8EF: B1 B6 LDA ($B6),Y A8F1: C8 INY A8F2: CA DEX A8F3: CA DEX A8F4: 9D 00 06 STA $0600,X A8F7: B1 B6 LDA ($B6),Y A8F9: 9D 01 06 STA $0601,X A8FC: 4C 9D 00 JMP $009D Both of these routine move a value to the top of the stack, just using different pointers to source the value ($C6 and $B6). Most of the remaining of the opcodes have unique implementations. Some of the interesting ones to look at are "load string" ($2C at $A88F), load small constants ($F0-$F7), load 1, 2 and 4 bytes ($24, $25, $26), call ($A2 at $AB2C) and return ($A6 at $AC96). What's most interesting to me is that having identified these, you might notice they don't match the "Functional Specification" (https://archive.org/details/AtariPascalFunctionalSpecification) at all. Apparently the paper design for the interpreter presented to Atari underwent major revisions by the time it was published. I expected some revisions, but it appears little of the original opcode design survived. In our next post, we'll examine the p-code that exists in the PASCAL runtime object, and write a very basic p-code disassembler.
  • Create New...