Pab Posted March 23, 2015 Author Share Posted March 23, 2015 When run, the edit window of the compiler executable contains this skeleton for those who want a qu MODULE ORG=$1F00 USES Print CARD c PROC MAIN RETURN The MODULE command, as a reminder, tells the compiler where in memory to put the code (and optionally variables) generated until the next MODULE is hit. The ORG statement says where to place the code. The optional VARIABLES statement will tell the compiler where to put variables for this section. If left out, variables will be allocated within the code as Action! does. Addresses in banked RAM are represented by the memory location, a colon,. and the bank number (1-64 supported by compiler, but subject to actual RAM in the machine obviously). Bank 0 always refers to the "main" bank of a stock machine. Examples: Compile placing code in main bank starting at $2000 and variables in main bank starting at $8000 MODULE ORG=$2000 VARIABLES=$8000 Compile both code and variables starting at $4000 in bank #2 of expanded memory. MODULE ORG=$4000:2 Compile code beginning at $A000 (for cartridge use, for example) and variables starting at $4000 in bank #1. MODULE ORG=$A000 VARIABLES=$4000:1 The USES PRINT statement will load the routines from PRINT.ACC at compile time. In the finished product, only routines needed will end up being compiled. If there is a procedure called MAIN, it will be run after the program file is run by DOS. Thus the skeleton of a procedure. Quote Link to comment Share on other sites More sharing options...
Alfred Posted March 24, 2015 Share Posted March 24, 2015 Why is CompilerUnit a separate project ? Was that just for early testing ? I use Delphi rather than Lazarus, but it looks like it should be easy to port over. Quote Link to comment Share on other sites More sharing options...
Pab Posted March 24, 2015 Author Share Posted March 24, 2015 CompilerUnit is separate because I didn't want to tie the thing to the GUI I had designed. It can be fairly easily pulled out and dropped into a new set of forms or a command line program. Quote Link to comment Share on other sites More sharing options...
Pab Posted April 5, 2015 Author Share Posted April 5, 2015 Bit of a milestone reached tonight: objects appear to be functional. Fields can be assigned within or outside of the class, methods can be called and can return data. Still need to work on and test descendant types, and will hopefully have a much more elaborate example once I'm sure things are actually working properly. MODULE ORG=$1F00 USES Print CLASS TObject BYTE B BYTE E PROC Init BYTE FUNC Query END PROC TObject.Init B = 10 E = $9B RETURN BYTE FUNC TObject.Query RESULT = B RETURN PROC MAIN TObject T,U BYTE x T.Init U.Init T.B = 35 U.E = 100 x = T.Query Print("This should be 35: ") PrintBE(x) Print("This should be 10: ") PrintBE(U.B) Print("This should be 100: ") PrintBE(U.E) Print("This should be 155: ") PrintBE(T.E) RETURN Right now things are a little kludgy when assigning values to fields from within an object method. When the compiler defines a class, it keeps track of all of the fields within the class and assigns each of them a "class offset" value, pointing to how many bytes into the class the field we want is. In my example program above, the class offset of "B" in a TObject instance is 0 (it's at the base address of the instance) and "E" has an offset of 1 (one byte higher than the base). If B had been a CARD, then E's offset would have been 2. And so on. When a field is accessed outside of the class's own methods, the compiler does the heavy lifting. In this program, "U" is located at $2289. When we assign 100 to U.E, the compiler knows that since E's offset is 1, so the variable we are accessing is at $228A. Simple operation, two opcodes. 23B7: A9 64 LDA #$64 23B9: 8D 8A 23 STA $238A When a field is accessed within the method, however, we don't know at compile time where the addresses we are going to be working with are. When an object method is called, the first argument (hidden to the user) is always a pointer to the instance we are going to be working with. To access the field, the machine adds the offset to the instance address via the micro-runtime's 16-bit addition routine. 22D6: AD 96 22 LDA $2296 22D9: 85 CB STA $CB 22DB: AD 97 22 LDA $2297 22DE: 85 CC STA $CC 22E0: A9 01 LDA #$01 22E2: 85 CD STA $CD 22E4: A9 00 LDA #$00 22E6: 85 CE STA $CE 22E8: 20 15 20 JSR $2015 Not very elegant to say the least. Can't yet think of a more efficient way. 1 Quote Link to comment Share on other sites More sharing options...
Pab Posted April 5, 2015 Author Share Posted April 5, 2015 (edited) Got arrays of objects to work this morning, and made a tweak to the definition of hard-addressed procedures. Then wrote a long post about something I'd done by accident then decided to keep in place before I thought "why are you doing that?" So I went and fixed the accident. I'm adding a compiler flag (probably "ACTIONARGS") to decide whether or not to load the first three bytes' worth of arguments into registers. Yeah, it only takes 6 bytes of code and a few machine cycles to do that, but why waste the time on every procedure call if you don't need to? That way you can just turn the flag on for procedure definitions or modules where you need them loaded. Remember also that there are already pseudovariables for the three hardware registers already in the language. Most programmers would probably want to do something like 6502_X = $10 CIOV than CIOV(0,$10) as the resulting code will be much tighter. (It would compile to LDX #10/JSR CIOV, very tight and fast.) Edited April 5, 2015 by Pab 1 Quote Link to comment Share on other sites More sharing options...
Pab Posted April 5, 2015 Author Share Posted April 5, 2015 Another example, which I mainly used to test class arrays. A very unnecessarily roundabout "Hello World" program. MODULE ORG=$1F00 CLASS TIOCB BYTE ID BYTE Number BYTE Command BYTE Status CARD Address CARD PutOne CARD Length BYTE ARRAY Aux[6] PROC Run(BYTE a,x) END // Array pointing to IOCB's // in RAM TIOCB ARRAY IOCB[8] = $340 // String in fixed spot since // I don't have pointers fully // functional yet. STRING s[128] = $0580 // Pass args in registers DEFINE ACTIONARGS PROC CIOV = $E456(BYTE a,x) UNDEFINE ACTIONARGS // End code using ActionArgs PROC TIOCB.Run(BYTE a,x) CIOV(a,x) RETURN PROC MAIN s = "Hello World" IOCB[0].Command = 11 IOCB[0].Address = $581 IOCB[0].Length = 11 IOCB[0].Run(0,0) IOCB[0].Length = 0 IOCB[0].Run(155,0) RETURN Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted April 5, 2015 Share Posted April 5, 2015 Thanks for these updates. Do you have any idea when the next test version will be available? Quote Link to comment Share on other sites More sharing options...
Pab Posted April 6, 2015 Author Share Posted April 6, 2015 I have one or two more things I want to tackle before I post another build. Probably in the next day or two. Quote Link to comment Share on other sites More sharing options...
danwinslow Posted April 6, 2015 Share Posted April 6, 2015 Yes, this looks very cool. Please keep us updated. Quote Link to comment Share on other sites More sharing options...
Pab Posted April 6, 2015 Author Share Posted April 6, 2015 Since I wanted to start doing some more elaborate programming (and figured some of you out there would too) to test this thing as I develop, I've spent the last couple of days working on I/O routines for the runtime package. I'll share them when I upload the latest build. Also put into place (sort of needed it if disk I/O is in the offing) some basic error handling in the runtime. New additions to the language's page 0 memory map include: $C5 - DEVICE - Default device # $C6 - LASTERROR - Error returned by CIO or other routines $C7/$C8 - ERRORVEC - Pointer to a routine to use in place of built-in error handling Also, like Action!, an EOF is NOT treated as an error, but just sets a flag. The end-of-file status can be checked in the boolean array EOF[x] Quote Link to comment Share on other sites More sharing options...
danwinslow Posted April 7, 2015 Share Posted April 7, 2015 (edited) Are you sure you want to burn a zp slot for something like LASTERROR and DEVICE? Unless you are just taking over the whole machine, it's easy to run into problems with zero page space. Edited April 7, 2015 by danwinslow Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted April 7, 2015 Share Posted April 7, 2015 Especially if FP is intended to be implemented in the future... Quote Link to comment Share on other sites More sharing options...
+Ripdubski Posted April 7, 2015 Share Posted April 7, 2015 nice work so far! Quote Link to comment Share on other sites More sharing options...
Pab Posted April 7, 2015 Author Share Posted April 7, 2015 You guys are right. There's really no benefit to using zero page for those locations since indirect jump (which is used with the error vector) needs a two byte argument anyhow, and since LastError and Device will be called so infrequently that the byte saved in each reference just isn't worth tying up those locations. Will do a little rewriting to move those values into ones allocated dynamically when using the I/O runtime. That leaves the only locations needed by the language as $A0-$AF and $C9-$D0. I only use the FP locations for scratch memory, such as values returned from the micro-runtime routines, so nothing will be lost if we choose to fully implement floats. I'm thinking about the possibilities of implementing 32-bit LONG variables, which might require more locations. Right now the micro-runtime routines use CB/CC and CD/CE for the operands, with CF and D0 being used generally for bank numbers when referring to locations in banked RAM. If I need to add 32 bit math, one operand would have to be from $CB-CE. I guess the second operand could be $CF-$D2, since bank numbers would never be needed in 32 bit operations. That way the result could be passed in $D4-$D7. I think in the long run we'd be better off implementing 32-bits than floats. Not that one rules out the other. Just implementing floats is turning out to be a big pain in the ass and the time I would invest in it would be better spent on LONGs. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted April 7, 2015 Share Posted April 7, 2015 (edited) +1 for 32 bit ints, providing they can be optionally signed. I don't usually miss FP and fractional results can generally be synthetically derived using integer math. Edited April 7, 2015 by flashjazzcat Quote Link to comment Share on other sites More sharing options...
Pab Posted April 7, 2015 Author Share Posted April 7, 2015 (edited) Here's an interesting question. About to write the RTL code for the XIO command, I was reminded of the weird syntax Action! used for its XIO routine: XIO(device,0,command,aux1,aux2,filespec) What was with the extra byte (which apparently was unnecessary because they said to just use a 0 there) between the device number and command? And other than backwards compatibility with Action! is there any reason to keep it? The one thing I can think of is that since Action! passed the first three bytes in the registers, this kept the X register free and put the command in the Y register. This way the routine could just ASL A four times, TAX, and STY ICCOM and not have to do anything with X. But if so, it's a kludgy way of doing things. Edited April 7, 2015 by Pab Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted April 7, 2015 Share Posted April 7, 2015 Um... couldn't be the buffer length, could it? It's the only thing not accounted for. Quote Link to comment Share on other sites More sharing options...
Alfred Posted April 7, 2015 Share Posted April 7, 2015 It's because the XIO branches in the RTL to the BPut/BGet I think, and it uses the zero to mark it as a non-data transfer operation, iirc. Quote Link to comment Share on other sites More sharing options...
TXG/MNX Posted April 8, 2015 Share Posted April 8, 2015 when will a first public beta be released ? Quote Link to comment Share on other sites More sharing options...
snicklin Posted April 8, 2015 Share Posted April 8, 2015 This is really coming along well. I've not used it before because it has been very much work in progress and thought that I might hit a few issues. It is looking like that a lot of those will be gone soon and there's lots of extra functionality. Can I ask, will there be a manual enclosed with this? Quote Link to comment Share on other sites More sharing options...
Pab Posted April 8, 2015 Author Share Posted April 8, 2015 As for the public beta, I'm not sure there will really be such a thing. The idea at this point is to build a bootstrap compiler, with which I (and hopefully some volunteers) can write a native compiler that will run on the Atari that anyone can use. I'm going to keep putting out alpha versions here for people to look at and play around with. One will be coming tomorrow if all goes well. As for the manual, I'll put together a language reference and tutorial, and docs for the RTL, but a full manual will probably have to wait until we're well into the native compiler. Quote Link to comment Share on other sites More sharing options...
Pab Posted April 8, 2015 Author Share Posted April 8, 2015 (edited) One thing I wanted to get fully functional before I put out the latest build, just because it was the first feature request I remember seeing that I knew I could get functional in a hurry: support for interrupts. It's now possible to write a procedure to be used as an interrupt simply by replacing the PROC keyword with INTERRUPT. Doing this will add code to push the registers onto the stack, and when a RETURN is reached, the registers will be popped off of the stack and an RTI used to return instead of RTS. Example program, quick and dirty DLI. MODULE ORG=$1F00 CARD DLIVector=$200 CARD DLAddress=$230 BYTE InterruptEnable=$D40E BYTE CH=$2FC BYTE ARRAY DisplayList[0] [$70 $70 $70 $42 $40 $9C $02 $02 $02 $02 $02 $02 $02 $02 $82 $02 $02 $02 $02 $02 $02 $02 $02 $02 $02 $02 $02 $02 $02 $41 DisplayList] INTERRUPT ColorShift BYTE COLPF2=$D018 COLPF2=$38 RETURN PROC MAIN CARD OldDisplayList // Stash the old DL address OldDisplayList = DLAddress // Set our interrupt vector DLIVector = @ColorShift // Zap in our new display list DLAddress = @DisplayList // Enable Interrupt InterruptEnable = 192 // Wait for a key CH = $FF WHILE CH=$FF DO OD // Go back to normal. InterruptEnable = 64 DLAddress = OldDisplayList CH = $FF RETURN Edited April 8, 2015 by Pab 2 Quote Link to comment Share on other sites More sharing options...
danwinslow Posted April 9, 2015 Share Posted April 9, 2015 nice! Quote Link to comment Share on other sites More sharing options...
Pab Posted April 9, 2015 Author Share Posted April 9, 2015 (edited) For your inspection and amusement, the latest build. I'm working on pointers at the moment, but they're giving me a headache so I thought I'd put out what I had. WHAT DOESN'T WORK (yet): * Pointers, for the most part. * Arrays within classes. Working on this, too. Need to test some stuff. * Error vector for custom error handling. Being rewritten to not require page zero location. * FLOATs. Still debating the need for them. * STRING FUNCs * Probably dozens of new bugs I added in but haven't detected yet. Sigh. Changelog for this version: GUI: ==== * Added ability to load/save source files. * Layout changes. COMPILER: ========= * Added object support. * Added ACTIONARGS flag to pass arguments in registers. * Fixed bug in assigning address of fixed location procedure. * Fixed bug in parsing multiple arguments in procedure calls. * Finalized support for INTeger variables (signed 16-bit) * Added !BANKPEEK, !BANKPOKE, and !BANKCOPY labels for ML code. * Fixed bug where function calls in assignments would skip over arguments. * Bug: Bank initialization code was never run unless code was placed in a bank. For the time being, the bank initialization routine is ALWAYS the first segment of a file, at least until we have two pass compilation and will know whether or not we need banked support. * Fixed bug in storing the address of a variable or procedure in another variable. (a = @b). If assigning to a CARD, the result will be the address (no bank information) of the variable. If assigning to a pointer, banking information will be included. RTL: ==== * Deprecated PRINT.ACC * New unit IO.ACC includes the following calls: PROC StrC(CARD c,STRING POINTER s) PROC StrB(BYTE b,STRING POINTER s) PROC StrI(INT i,STRING POINTER s) PROC XIO(BYTE d,scrap,c,m,a STRING f) PROC Open(BYTE d STRING f BYTE m,a) PROC Close(BYTE d) BYTE FUNC GetD(BYTE d) BYTE FUNC Get(BYTE d) PROC PutD(BYTE ICB,a) PROC PrintD(BYTE ICB, STRING s) PROC PrintDE(BYTE ICB, STRING s) PROC Print(STRING s) PROC PrintE(STRING POINTER s) PROC Put(BYTE b) PROC PrintC(CARD c) PROC PrintCE(CARD c) PROC PrintID(BYTE d INT i) PROC PrintI(INT i) PROC PrintIDE(BYTE d INT i) PROC PrintBD(BYTE d,b) PROC PrintB(BYTE b) PROC PrintBE(BYTE b) PROC PrintBDE(BYTE d,b) CLASS TIOCB BOOL ARRAY EOF[8] BYTE device BYTE LastError TIOCB ARRAY IOCB[8] = $340 AccomplishCompiler 0-2-0-14.zip Edited April 9, 2015 by Pab 2 Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted April 9, 2015 Share Posted April 9, 2015 Yikes I didn't even realize this was gonna be an IDE - I expected just a command line compiler. Looking really nice! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.