Re: A89: Re: porting Z80 to 68k.....possible?? (yes, but not easy)


[Prev][Next][Index][Thread]

Re: A89: Re: porting Z80 to 68k.....possible?? (yes, but not easy)




I have only 3 points to make:

1) What about a routine like this to access data?
   ld a,(level)
   ld b,a
   ld de,49 ;Size of a level
   ld hl,levelSet+1-49 ;levelSet-48
lvlAddLoop:
   add hl,de
   djnz lvlAddLoop

49 is a odd number... and this is a program that will eventually be on
83s
2) What about people who write self-modifying code like this:
   .db $FE; cp n
levelCountCheck .db 0

3) What about actual data that is accessed by (label+1)?

Patrick Davidson wrote:
> 
> > Why is it crazy??  There are similar instructions on both processors,
> > the 68k has more registers so you don't have to worry about running
> > out, and the languages are pretty much the same (I've programmed in
> > both and they are very similar).  I don't see any difficulties except
> > for the memory locations which would be easily switched between calcs.
> > I dunno, maybe I'm just stupid and I don't know anything about this,
> > but from my POV, it looks like a good idea.
> 
> Actually, I have been working on this very problem myself; I am making
> a computer program to automatically convert TI-85 programs to run on
> the 68K calculators.  I do think I have (more or less) solved most of
> the problems involved, but it's much more difficult than you seem to
> suggest.  My program will only work from Z80 source code, and should do
> both ZShell and Usgard programs.  Since it's just a simple translator,
> the output won't be as efficient as code written for the 68K, but since
> the 68K is a faster processor it should allow programs to run somewhere
> near the correct speed.  Anyway, here's a list of the things the program
> needs to do:
> 
> 1) Parse Z80 source code.  Recognize instructions, directives,
> registers, etc.  This part is straightforward.
> 
> 2) Basic code translation.  I'm only referring to the basic instructions
> here, and not memory access or control stuff which is addressed later.
> This means to take a Z80 instruction and write out a 68K equivalent.
> This is not as easy as you make it seem, as the registers are not that
> similar.  The difficulty has to do with the fact that registers can be
> split and combined (e.g. B, C, and BC).  Anyway, this is my plan for
> dealing with this:
> 
> The register equivalencies will be this (there is a reason for these
> particular choices:  D0 and D1 are scratch since library calls may
> change them, and the address registers are in D2 to D6 so they can be
> conveniently mirrored in A2 to A6, as is explained later).
> 
> D7 = A
> D2 = BC
> D3 = DE
> D4 = HL
> D5 = IX
> D6 = IY
> 
> In these registers, I do plan to keep them in big-endian order as is
> normal in the 68K, and will just reverse the order (with ror.w #8,dn)
> when storing them to memory.
> 
> The 68K can deal with the low word and low byte of a register as
> seperate values, but not so with the second byte.
> 
> For example, the one instruction
> 
>    ld c,a
> 
> becomes the one instruction
> 
>    move.b d7,d2
> 
> while the one instruction
> 
>    ld b,a
> 
> must become multiple instructions, such as
> 
>    move.b d7,d0
>    lsl.w #8,d0
>    and.w #$ff,d2
>    or.w d0,d2
> 
> However, some special cases such as the instruction
> 
>    dec b
> 
> can still work in one instruction
> 
>    sub.w #$100,d2
> 
> I have considered some systems in which each component of a
> register would have its own 68K equivalent, in which case only
> word operations would need special tricks, but I decided not to do
> this in the first version of the translator (though I might make it
> an option later).
> 
> Of course, alignment is also a problem as the 68K needs instructions
> on even addresses.  This is not, however, a huge problem.  As a
> temporary solution, I'll just write "EVEN" whenever there is a change
> from data to instructions.  As far as loading words from memory, I'll
> simply load each byte a time to avoid trouble.
> 
> 3) Memory access - Unfortunately, this is not simply a matter of
> finding equivalent addresses.  Most addresses on the TI-85 have no
> equivalent on the 68K calcs (and the situation is much worse on the
> TI-86).  The solution I have come up with is as follows:
> 
> Z80 memory addresses         Where they go
> 
> $0000-$7FFF                  nowhere
> $8000-$8FFF                  Temporary 4K buffer
> $9000-$EFFF                  program's code
> $F000-$FFFF                  Temporary 4K buffer
> 
> This memory arrangement allows all low memory variables to work,
> and also allows tricks with the stack.  In addition, programs that
> allocate memory from the top of the VAT (e.g. grayscale buffers)
> could still work.  Mapping another area to the program's code
> allows the program to reference data in itself (which is very
> common) and allows a possibilty to get self-modifying code to work.
> This does have the downside that any program which is larger than
> 24K when converted will fail.
> 
> (If you're wondering, I did consider other memory systems before
> coming to conclude that this one is best.  At first I thought I
> would just try to convert addresses, but realized that this couldn't
> work.  The fact that addresses are different sizes, can be part of
> data structures, and can be "built" byte by byte (e.g. calculating
> H and L seperately, then combining to get a pixel address) and the
> fact that a value like $FFFF could easily used in calculations as
> -1 or the lower right corner of the LCD, or both, and other things
> led me to think against simple translations).
> 
> I'd also need to do something with labels to get the fake addresses.
> I'd have to labels corresponding to each original; one that's its
> real 68K address (for program branches) and another that's the fake
> virtual address for other references.
> 
> This virtual addressing isn't 100% perfect, so converted programs
> need to be extensively tested befor they are considered stable, even
> when the original is reliable.  I'm of course trusting that programs
> won't try to write to the ROM.
> 
> This also is a major performance drain, but I think that memory
> references are not used so much that this will be unacceptable.  I
> have come up with several different methods of determining addresses
> that have somewhat different performance issues:
> 
> a) Calculate-on-demand.  Whenever you reference a memory address, the
> real address will be determined.  This is more efficient if the
> program uses the address registers as data a lot, but will waste time
> when the same address is accessed repeatedly.
> 
> b) Calculate-on-change.  Whenever the address register is changed,
> you re-do the address conversion.  The real addresses would be
> shadowed in address registers. If the register is simply incremented
> or decrementing, you only need to increment or decrement it instead
> of the whole recalculation.  This is faster if the same address is
> used a lot, or the program steps through addresses.
> 
> c) Validity flags.  In this method, I'd have some sort of flag
> indicating which registers had valid shadows.  When a register is
> changed, it's set to invalid.  Then any memory access would check
> the flag, and if invalid, recalculate and set the flag.
> 
> d) Some combination of the above, such as (b) for IX and IY which are
> almost always addresses, with (a) for the others which are often data.
> 
> In the first release, I've decided to use method (a) though I may
> make the others options later, depending on how well it turns out to
> work.
> 
> 4) "Emulation" library - ROM calls, display, ports
> 
> These features would go together in an emulation library that the
> converted program uses.
> 
> The display handling would be fairly simple: just an interrupt that
> copies data from the simulated memory area to the actual screen
> periodically.
> 
> The port handler would most importantly handle keyboard reads.
> Other ports could mostly be ignored for most programs, though at
> some point the display address port could be activated to allow
> grayscale.
> 
> The ROM call emulation is possibly the hardest.  However, it is
> survivable.  Since the converter has the program's source code it
> can easily identify ROM calls.  The library would need to simulate
> their action.  Some, like UNPACK_HL would be easy (and actually
> faster than on the original TI-85).  The display routines would be
> more difficult; in addition to reading the coordinates from the
> virtual address space, you'd have to somehow put the letters back
> over the virtual screen area (I haven't thought about this in
> detail yet, but it shouldn't be too bad).
> 
> The most important feature of the library, of course, would be to
> display a message warning the user that a converted program may be
> unstable before the program begins, allowing a safe exit for people
> who unknowingly run such a program.
> 
> 5) Program flow control - For the most part, this one is actually
> easier than it seems.  Conditional branches and stuff within the
> code are easily converted to 68K equivalents in most cases.  A jump
> to a computed address could be dealt with by the normal memory
> handling system.  For example, this jump table would work fine:
> 
>           ld a,(hl)
>           add a,a
>           ld e,a
>           ld d,0
>           ld hl,&address_table
>           add hl,de
>           call LD_HL_MHL
>           ld de,(PROGRAM_ADDR)
>           add hl,de
>           jp (hl)
> address_table:
>           .dw addr0
>           .dw addr1
>           .dw addr2
> 
> Unfortunately, this one would fail miserably:
> 
>           ld a,(hl)
>           ld l,a
>           add a,a
>           add a,l
>           ld e,a
>           ld d,0
>           ld hl,&address_table
>           add hl,de
>           jp (hl)
> address_table:
>           jp &addr0
>           jp &addr1
>           jp &addr2
> 
> This is very bad, since something is multiplied by 3 to get an
> address, while instructions on the 68K can't be at odd addresses.
> Even if it could, it would still fail as the addresses aren't lined
> up properly.  This would require the following *hideous kluge*
> 
> - Jump table detector in the converter which notices any string of
>   three or more jumps
> - Jump table list at the end of the code, which holds the addresses
>   each jump table covers
> - A much uglier memory address handler for computed jumps:  It would
>   need to check the address you jump to against these tables, and do
>   the appropriate conversion if its present.  That is, if it's in
>   the jump table's range, subtract the start address, divide by three,
>   and then compute the real address with the offset value.
> - Even the "kinder, gentler" jump tables with JR instructions would
>   still need this kluge, since the instructions might expand enough
>   that the branches wouldn't fit in two bytes.
> 
> As you may have guessed by now, I'm not going to put this capability
> in the first release of the converter, as it's a mess and not that
> useful.
> 
> 6) Self-modifying code.
> 
> Self-modifying code is common in Z80 programs, so it needs to be
> dealt with.  This is actually, easier than it seems, as most of it
> is in the form of stuff like "ld (&label+1),a" which is simply used
> to change the constant data of an instruction.  This can be detected
> by looking for writes to 1 beyond a code label.  Then the converter
> simply needs to determine the real offset into the new code, and
> replace it.
> 
> However, this is not the only self-modifying code that's ever been
> used, even though it's the most common.  For example, in Z-Kart 3D,
> I change the an opcode between a RET and a NEG.  Since this sort of
> thing is probably rare, I'll deal with it by implementing specific
> handling for each of the few cases of it I see.  Of course, the
> first release will only do the standard stuff above.
> 
> 7) The hard part.  Once everything above has been done and tested,
> then I can release a "workable" version of the program.  However,
> the above is only the easy part.  Difficult stuff includes:
> 
> - Interrupts/grayscale (actually, this one isn't too bad)
> - Variables
> - Dynamically constructed jump tables (Orzunoid uses this to help
>   with external levels; the first converted release of Orzunoid
>   will be from an older version of the game which doesn't do this)
> - Programs that throw code around (e.g. Z-Kart 3D which copies its
>   code to the graph screen)
> - Dealing with multiple calculators: the TI-82 and TI-83 probably
>   wouldn't be that hard to implement, but TI-86 is a real mess due to
>   its memory swapping and such
> - And there's probably more stuff that's so horrible I can't even
>   think of it...

-- 
Scott "_Wrath_" Dial
wrath@calc.org
ICQ#3608935
TimeCity AI Guy II - www.timecity.org
Member of TCPA - tcpa.calc.org
__________________________________________
NetZero - Defenders of the Free World
Get your FREE Internet Access and Email at
http://www.netzero.net/download/index.html


Follow-Ups: References: