Image Map Image Map
Page 1 of 2 12 LastLast
Results 1 to 10 of 14

Thread: The running PDP-8/SBC6120 software project/questions thread

  1. #1

    Default The running PDP-8/SBC6120 software project/questions thread

    So I'm dusting off something I started on a year or so back - I had wanted to cobble together a modest game project to demo on my SBC6120 at VCF West. Unfortunately, other stuff piled up on my to-do list and I ended up missing it anyway due to car trouble. I'd like to see if I can actually make a go of it this year, so I'm starting a thread in the hopes that that'll help keep me a little more on-task. Not a whole lot to share at present other than the basics: the aim is to throw together a simple roguelike, since that's something well-suited to terminal I/O, just a little ways out from being period-appropriate (the development of Rogue began around 1980 which was past the -8's heyday, but there were still plenty out in the field,) and not too taxing (I figure the game state for a simple one could fit comfortably within one 4K field, though text and program code are another matter.)

    For a couple reasons (I want to keep code size down so there's more room for other stuff, I'd like to abstract it out a bit for portability, and I don't want to get too bogged down in PDP-8 assembler optimization,) I'm going to be building this on top of a simple interpreter (not bytecode..."wordcode?") implementing a basic stack machine, with performance-critical or overly juggly bits done in assembler. I'm currently in the middle of building that; I have a modest chunk of the core opcodes implemented, plus a few more complex utility routines, but there's still a bit left to go on that before I start the real work of actually building the game program.

    While I'm working on that, though, I thought I'd see if any of the -8 enthusiasts 'round here wanted to weigh in on one of the bits that I had to think for a bit before coming up with a decent solution for: arithmetic-right-shift. My first attempt was pretty clunky and involved a frustrating amount of register-juggling, but after sitting down and having a good think about it, I was able to cut the process down to four instructions (minus the general overhead.) Just curious if anyone knows a better way to do it - here's what I've got now:
    Code:
    	CLA CLL CML RAR			/ Load AC with 4000
    	TAD variable			/ Load/add the operand
    	/ L now contains the original MSB, while the real MSB is inverted
    	RAL				/ The inverted MSB is now in L
    	CML RTR				/ Normalize the MSB and shift into place
    Computers: Amiga 1200, DEC VAXStation 4000/60, DEC MicroPDP-11/73
    Synthesizers: Roland JX-10/SH-09/MT-32/D-50, Yamaha DX7-II/V50/TX7/TG33/FB-01, Korg MS-20 Mini/ARP Odyssey/DW-8000/X5DR, Ensoniq SQ-80, E-mu Proteus/2, Moog Satellite, Oberheim SEM
    "'Legacy code' often differs from its suggested alternative by actually working and scaling." - Bjarne Stroustrup

  2. #2

    Default

    Quote Originally Posted by commodorejohn View Post
    I was able to cut the process down to four instructions (minus the general overhead.) Just curious if anyone knows a better way to do it - here's what I've got now:
    Code:
    	CLA CLL CML RAR			/ Load AC with 4000
    	TAD variable			/ Load/add the operand
    	/ L now contains the original MSB, while the real MSB is inverted
    	RAL				/ The inverted MSB is now in L
    	CML RTR				/ Normalize the MSB and shift into place
    Your solution looks good. The rest of this is just spit balling ideas. Maybe you will see something in it you didn't think of.

    The first solution that came to mind:
    Code:
            CLA CLL
            TAD variable
            SPA
            CML
            RAR
    This is one instruction longer than your solution unless we can assume that the AC and L are initially clear which makes them the same length but the positive variable path is one instruction execution shorter. A lot of optimization depends on what comes before since the way the AC and link were left by the previous operation is important. If the variable happens to be in the AC already then this works:

    Code:
            CLL
            SPA
            CML
            RAR
    This is the fastest code sequence to copy the sign bit into the L without mucking up the AC.

    One other solution I came up with is not particularly good but there might be a situation where the idea works:
    Code:
            CLA
            TAD variable
            TAD variable
            CLA
            TAD variable
            RAR
    And this is the same as the previous but slightly faster:
    Code:
            CLA
            TAD variable
            RAL
            CLA
            TAD variable
            RAR
    Good luck with your project! I look forward to seeing it.
    Doug Ingraham
    2nd owner of Straight 8 SN1173
    5 other PDP-8's including an 8/i and a DECSet 8000
    SOL-20

  3. #3

    Default

    Yeah...unfortunately, while AC is known to be clear going in, L is unknown (I'd have to add an instruction to the dispatch routine to clear it, and that'd just slow things down for most opcodes where it doesn't even matter.) The first alternative would still work, though.
    Computers: Amiga 1200, DEC VAXStation 4000/60, DEC MicroPDP-11/73
    Synthesizers: Roland JX-10/SH-09/MT-32/D-50, Yamaha DX7-II/V50/TX7/TG33/FB-01, Korg MS-20 Mini/ARP Odyssey/DW-8000/X5DR, Ensoniq SQ-80, E-mu Proteus/2, Moog Satellite, Oberheim SEM
    "'Legacy code' often differs from its suggested alternative by actually working and scaling." - Bjarne Stroustrup

  4. #4

    Default

    Sounds like you are writing an interpreter. One of the speed ups I came up with is placing the start of the dispatch routine on page zero. That way you can jump directly there saving 1.2 microseconds by eliminating the defer portion of the JMP instruction cycle. A defer cycle adds 1.2 us on the 6120 CPU. It is worse on all he other CPU's. You might also consider placing the interpreters PC in one of the auto increment locations although I was never able to make this work to my advantage since the auto increment takes place before the defer.

    I wrote an 8080 emulator in 1976 and finished debugging it about 2 years ago when a paper tape copy resurfaced. I then spent a considerable amount of time speeding it up. It runs 8080 code on the 8/a at about 1/60th the speed of a 2 mhz 8080. It runs MITS basic reasonably.

    There can be a huge speed advantage to using the PC in the PDP-8 to keep track of the execution and eliminate the dispatch code. Every instruction turns into a JMS to a destination on page 0 or a JMS I through an address on page zero. The downside to this approach is that your program ends up all living on the same field so ends up limited to 4k code. If you treat is as a Harvard architecture and limit the code space to 4k the rest of memory is your data space. This might work for your project.
    Doug Ingraham
    2nd owner of Straight 8 SN1173
    5 other PDP-8's including an 8/i and a DECSet 8000
    SOL-20

  5. #5

    Post

    Yeah, the zero-page dispatch and auto-increment IP are good notions - had those in place already. Didn't know about auto-increment taking place before the defer, though - I don't think that should be an issue, but it will mean needing to add a fix-up instruction to the jump/call routines.

    Subroutine-threaded code is an interesting notion - it limits you to 128 opcodes, but that's not a huge deal; the other downside is that I wouldn't be able to use the scheme I'm currently using where the MSB of the opcode determines whether it's a constant or an instruction, so every constant push would require a preceding instruction - plus, if I'm understanding the extended addressing scheme correctly, it'd require a copy of the interpreter in every program field. But it would represent a major speed-up...I'll have to have a think on that.

    For reference, this is the dispatch code as it currently stands:
    Code:
    const,	/ CONST routine - pushes opcode in AC onto the stack as a constant
    	mql					/ save the value for later
    	cla cma					/ load AC with -1
    	tad z psp				/ load/decrement stack pointer
    	dca z psp				/ save the new pointer
    	swp					/ retrieve the value
    	dca i z psp				/ save it to the stack
    	/ falls through to:
    next,	/ NEXT routine - assumes AC is clear on entry
    	cdf 1					/ set data field to current program location
    	tad i z ip				/ get the next opcode
    stkfld,	cdf 0					/ select the stack field
    	sma					/ if the MSB is clear,
    	jmp z const				/ push it as a constant
    	dca z tmp				/ otherwise, save it as a dispatch address
    	jmp i z tmp				/ and dispatch
    Last edited by commodorejohn; January 24th, 2020 at 06:27 AM.
    Computers: Amiga 1200, DEC VAXStation 4000/60, DEC MicroPDP-11/73
    Synthesizers: Roland JX-10/SH-09/MT-32/D-50, Yamaha DX7-II/V50/TX7/TG33/FB-01, Korg MS-20 Mini/ARP Odyssey/DW-8000/X5DR, Ensoniq SQ-80, E-mu Proteus/2, Moog Satellite, Oberheim SEM
    "'Legacy code' often differs from its suggested alternative by actually working and scaling." - Bjarne Stroustrup

  6. #6

    Default

    Quote Originally Posted by commodorejohn View Post
    For reference, this is the dispatch code as it currently stands:
    Code:
    const,	/ CONST routine - pushes opcode in AC onto the stack as a constant
    	mql					/ save the value for later
    	cla cma					/ load AC with -1
    	tad z psp				/ load/decrement stack pointer
    	dca z psp				/ save the new pointer
    	swp					/ retrieve the value
    	dca i z psp				/ save it to the stack
    	/ falls through to:
    next,	/ NEXT routine - assumes AC is clear on entry
    	cdf 1					/ set data field to current program location
    	tad i z ip				/ get the next opcode
    stkfld,	cdf 0					/ select the stack field
    	sma					/ if the MSB is clear,
    	jmp z const				/ push it as a constant
    	dca z tmp				/ otherwise, save it as a dispatch address
    	jmp i z tmp				/ and dispatch
    I am guessing you haven't run this yet. CDF 1 won't do what you want. It is CDF 10 to select field 1. The opcodes are built with a logical or of the individual codes. With CDF the encoding is 62n1 where the selected field replaces the n. CDF 1 would generate 6201 the same as CDF 00 which selects field 0. CDF 10 will give you 6211 which selects data field 1.

    I am assuming that ip is in one of the auto increment registers. If I understand correctly your interpreter code is in the upper half (4000 - 7777) of some field. You can generate the jump/call destination addresses as destination-1 and then you won't have to patch up the destination on the fly when you load the new ip.

    This bit is well coded. I tried several variations and was not able to improve on it. My only comment is the use of the MQL and SWP which don't exist on the 8/s, 8/l or on other machines that don't have an EAE. Not certain if MQ functionality is present on 8/i or 8/e without EAE but I think it is there on the 8/a (M8315 CPU). It is not that big a deal to fix if someone wanted to run on a different platform.

    It was pointed out to me a few years ago that the Z (page zero selector) is not needed and is in fact ignored by pal and that I was a luddite for using it. My personal feeling is that PAL should recognize the Z and issue a warning if you specify a Z for a variable that is not in page 0. It should also issue a warning if the Z is not given and the variable is on page 0. But that is not the way it works but I continue to use the Z where appropriate and someday I will write a PAL with better error checking. PAL lets you do things that can't work without any notifications.

    Palbart doesn't care about case. I never tried to feed lower case into real PAL. I wonder it it would work? Your code looks strange to me because it is in lower case.
    Doug Ingraham
    2nd owner of Straight 8 SN1173
    5 other PDP-8's including an 8/i and a DECSet 8000
    SOL-20

  7. #7

    Default

    Hah, yeah, I haven't even tried a build yet as I still have more pretty core stuff left to go - but as regards the CDF instruction, you're right that it's wrong; init code and dedicated far-jump/far-call instructions are meant to replace that with a properly formatted field change to the current program field. Though I suppose it might as well be a correctly-formatted wrong initial value. As far as the EAE instructions go, my initial target is the 6120 (and the 8/e, if anybody out there should happen to want to try this when it's functional,) so I'm not concerning myself with other models at the moment - but, as you point out, it's fairly trivial to modify the code to not use the extra registers, especially since the real program is going to be running on top of this abstraction. Just adds more juggling with temporary variables into the equation, is all.

    Anyway, the more I think about the subroutine-threaded approach you brought up, the more I think I'm going to redesign around that; the lack of a shortcut for constants is a bummer, but in practice any negative/large unsigned numbers would require a separate instruction anyway. Having to keep a copy of the interpreter in every program field is possibly a bigger deal, but looking at the average opcode-implementation routine I don't think the core interpreter code is going to end up that large - and the speed gain from reducing the whole dispatch routine from eight instructions down to two should be substantial, even given that they're both deferred.
    Last edited by commodorejohn; January 24th, 2020 at 05:47 PM.
    Computers: Amiga 1200, DEC VAXStation 4000/60, DEC MicroPDP-11/73
    Synthesizers: Roland JX-10/SH-09/MT-32/D-50, Yamaha DX7-II/V50/TX7/TG33/FB-01, Korg MS-20 Mini/ARP Odyssey/DW-8000/X5DR, Ensoniq SQ-80, E-mu Proteus/2, Moog Satellite, Oberheim SEM
    "'Legacy code' often differs from its suggested alternative by actually working and scaling." - Bjarne Stroustrup

  8. #8

    Default

    I saw some code a long time ago and came across one of those things that I have used ever since. In a place where the code gets modified like a computed CDF or the entry point to a subroutine this guy used .-. as the placeholder. It is a visually distinctive thing and the code that gets generated for it is a 0.
    Code:
            *200
    START,  JMS JUNK
            JMP I C7605     /QUICK RETURN TO OS/8
    C7605,  7605
    
    JUNK,   .-.             /JUNK ENTRY POINT RETURN ADDRESS
            JMP I JUNK      /RETURN
            $
    Looks like that. Whenever you see that you know it is going to get overwritten.
    Doug Ingraham
    2nd owner of Straight 8 SN1173
    5 other PDP-8's including an 8/i and a DECSet 8000
    SOL-20

  9. #9
    Join Date
    Sep 2019
    Location
    Zurich, CH
    Posts
    109

    Default

    Quote Originally Posted by DougIngraham View Post
    I am guessing you haven't run this yet. CDF 1 won't do what you want. It is CDF 10 to select field 1. The opcodes are built with a logical or of the individual codes. With CDF the encoding is 62n1 where the selected field replaces the n. CDF 1 would generate 6201 the same as CDF 00 which selects field 0. CDF 10 will give you 6211 which selects data field 1.

    I am assuming that ip is in one of the auto increment registers. If I understand correctly your interpreter code is in the upper half (4000 - 7777) of some field. You can generate the jump/call destination addresses as destination-1 and then you won't have to patch up the destination on the fly when you load the new ip.

    This bit is well coded. I tried several variations and was not able to improve on it. My only comment is the use of the MQL and SWP which don't exist on the 8/s, 8/l or on other machines that don't have an EAE. Not certain if MQ functionality is present on 8/i or 8/e without EAE but I think it is there on the 8/a (M8315 CPU). It is not that big a deal to fix if someone wanted to run on a different platform.

    It was pointed out to me a few years ago that the Z (page zero selector) is not needed and is in fact ignored by pal and that I was a luddite for using it. My personal feeling is that PAL should recognize the Z and issue a warning if you specify a Z for a variable that is not in page 0. It should also issue a warning if the Z is not given and the variable is on page 0. But that is not the way it works but I continue to use the Z where appropriate and someday I will write a PAL with better error checking. PAL lets you do things that can't work without any notifications.

    Palbart doesn't care about case. I never tried to feed lower case into real PAL. I wonder it it would work? Your code looks strange to me because it is in lower case.
    The 8/E also have the MQ, and the instructions directly related to that even without an EAE. Same as the 8/A.
    The 8/I do not.
    Also the SWP only exists on 8/E and newer.

    The thing with the Z flag is a bit more complicated. Depending on which version of PAL you are using, explicit Z might or might not be needed. PAL8, which is used in OS/8 do not need the Z indication. It's only there to be backwards compatible with other PAL versions. PAL8 explicitly checks if the high 5 bits of an address is 0, and if so, it sets the Z flag in the instruction. The Z indicator itself is ignored, and you will not get any warning if it is used incorrectly.
    A little twist to all this is that the 6120 is "funny" in that the index registers (address 10 to 17 in page 0) only acts properly if you have the Z flag set in the instruction. So if you have code which actually resides in page 0, and which do indirect references through the index registers, and which do not have the Z flag set, then the registers are not incremented. Other PDP8 models do increment in this situation.

    Anyway, bottom line, Z is not really needed or used in PAL8, and only is sortof silently accepted in code to be backward compatible.

  10. #10

    Default

    Interesting. Might as well leave it there for clarity/just in case, I figure. Trying to figure out how that 6120 example would work, though - you'd have to be running out of code located in ZP and do an indirect reference through 10-17 in the program (actually zero) page?
    Computers: Amiga 1200, DEC VAXStation 4000/60, DEC MicroPDP-11/73
    Synthesizers: Roland JX-10/SH-09/MT-32/D-50, Yamaha DX7-II/V50/TX7/TG33/FB-01, Korg MS-20 Mini/ARP Odyssey/DW-8000/X5DR, Ensoniq SQ-80, E-mu Proteus/2, Moog Satellite, Oberheim SEM
    "'Legacy code' often differs from its suggested alternative by actually working and scaling." - Bjarne Stroustrup

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •