apersson850 Posted January 16 Author Share Posted January 16 (edited) Sure you can. In the assembly part of the program, you can see the location of the 80 column screen (2000H - 27FFH). You have to scroll that screen, not the one in VDP RAM, then call BL @47D2H to invoke the operatings system's routine to update the current 40 column window with the content of the relevant part of the 80 column screen. See, not even that is as simple as one may think! But scrolling a part of a screen in ordinary RAM is of course easier than doing it in VDP RAM. You can write your scroll routine in a separate source file, and assemble it separately, then link both that and the file I uploaded into the Pascal host unit. The linker can handle multiple assembly files. I probably have my own scroll routine as a part of some other file. Just couldn't locate it. I have another Pascal utility, which allows you to show a temporary message window on the screen. It then disappears by itself, since it's drawn in the VDP screen, not the 80 column screen. So as soon as you for example press FCTN- Screen right, it goes away again. If you like, I can show that too. Edited January 16 by apersson850 1 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 16 Author Share Posted January 16 Meanwhile, for you who use your TI for programs with text output in a different language than English, here's my solution to get the special Swedish characters. It can be adapted to any language, of course. To use it, you simply include uses swedish; in your program. Then, using the initialization and termination code sections of the unit, it will start by defining the Swedish character patterns, then clean up and restore the original ones on exit. Note that the interface part of this unit is empty. There are no procedures that the user can call. It just automatically changes the characters. unit swedish; (* Defines Swedish characters, and restores original characters *) (* A-DATA 860205 *) interface implementation uses support; var charsave:array[1..6] of string[16]; begin (* Swedish *) (* Save original characters *) get_pattern( 91,charsave[1]); get_pattern( 92,charsave[2]); get_pattern( 93,charsave[3]); get_pattern(123,charsave[4]); get_pattern(124,charsave[5]); get_pattern(125,charsave[6]); (* Swedish characters *) set_pattern( 91,'280038447C4444'); set_pattern( 92,'28003844444438'); set_pattern( 93,'100038447C4444'); set_pattern(123,'0028003848483C'); set_pattern(124,'00280038444438'); set_pattern(125,'0010003848483C'); ***; (* Restore characters *) set_pattern( 91,charsave[1]); set_pattern( 92,charsave[2]); set_pattern( 93,charsave[3]); set_pattern(123,charsave[4]); set_pattern(124,charsave[5]); set_pattern(125,charsave[6]); end. (* Swedish *) Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 16 Author Share Posted January 16 When at it anyway... This is my take on fixing the inability of the original dformat program to handle all sorts of disks, that is single and double sided as well as single and double density. Diskformat.pdf Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 17 Share Posted January 17 Has anyone tried using the RS232 to transfer a binary file from a PC to the Pascal system? I need to transfer a couple of binary data files (pattern and color files for a picture) to a UCSD Pascal disk for a project of mine, but there seem to be no modern tools capable of doing this. Pcode tool looked promising as it has an option to transfer data files but when I tried it the files were corrupted and of the wrong size. My thinking is to use the REM: device which is 300 baud by default, connect the PC to the TI RS232 via a straight serial cable, and use the Transfer command in the Filer to receive the file. The problem will be to figure out on the PC end how to send that file since all terminal emulators use some kind of file transfer protocol like Kermit or Zmodem. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 17 Share Posted January 17 This might too ambitious but this xfer program says it is an Xmodem transfer program in Pascal. Ureal what you can find on Github. pascal_sources/comm/xfer10.zip at master · codersclub/pascal_sources (github.com) Files in this archive: XFER.PAS Main source code file. Contains constant definitions, CRC calculation code and command line parameter processing. SXRX.INC Include file for XFER.PAS. Contains functions to send and receive Xmodem. XFER10.DOC This documentation file. XFER.EXE The complete compiled version, in case you don't have the IBMCOM unit or Turbo Pascal.</PRE> Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 17 Author Share Posted January 17 8 hours ago, Vorticon said: Has anyone tried using the RS232 to transfer a binary file from a PC to the Pascal system? I have limited my experiments of this kind to text files. One reason is that they are easier to inspect manually and correct if there is some issue with the transfer. It's of course possible to write a program which converts a binary file to some text file version and then another that does that conversion backwards again at the receiving end. Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 17 Share Posted January 17 I connected with the author of Pcode Tool @Rhodanaj and it seems it might be possible to add a proper binary file transfer functionality to Pcode Tool. He said he'll start working on it. Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 17 Share Posted January 17 1 hour ago, apersson850 said: I have limited my experiments of this kind to text files. One reason is that they are easier to inspect manually and correct if there is some issue with the transfer. It's of course possible to write a program which converts a binary file to some text file version and then another that does that conversion backwards again at the receiving end. I wonder if BLOCKREAD would work since it does not care about the kind of data it receives. Set up an array of bytes as the receiving buffer and REM:datafile as the input file and set the block number to transfer to 12. I suspect timing would be an issue though... It does not look like a proper terminal program capable of binary file transfers has been made for UCSD Pascal that I can find anyway. Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 17 Share Posted January 17 19 hours ago, apersson850 said: Sure you can. In the assembly part of the program, you can see the location of the 80 column screen (2000H - 27FFH). You have to scroll that screen, not the one in VDP RAM, then call BL @47D2H to invoke the operatings system's routine to update the current 40 column window with the content of the relevant part of the 80 column screen. I have another Pascal utility, which allows you to show a temporary message window on the screen. It then disappears by itself, since it's drawn in the VDP screen, not the 80 column screen. So as soon as you for example press FCTN- Screen right, it goes away again. If you like, I can show that too. Scrolling the 80 col screen per your description should be fairly straightforward. Given that UCSD Pascal IV supports concurrency, I can imagine an application where 2 separate processes are running at the same time, each using half of the screen. The user can then switch instantly between the 2 halves to see updates. Pretty cool I'd love to see the source for the message window. Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted January 17 Share Posted January 17 I see that you are saving 6 registers in the interrupt workspace at >83C0 and then restoring them later. (SAVEPAD is >83C6 which is R3, and STDVDPR1 is >83D4 which is R10) In XB you can just ignore that workspace and let the interrupt routine take care of it. It sounds like Pascal is using that workspace for other things besides to the usual interrupt routines. LWPI SCANWS ;SAVE SOME OF THE RAM PAD LOCATIONS LI R0,SAVEPAD MOV *R0+,R1 MOV *R0+,R2 MOV *R0,R3 LI R0,STDVDPR1 MOV *R0+,R4 MOV *R0+,R5 MOV *R0,R6 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 17 Share Posted January 17 (edited) 15 hours ago, Vorticon said: Has anyone tried using the RS232 to transfer a binary file from a PC to the Pascal system? I need to transfer a couple of binary data files (pattern and color files for a picture) to a UCSD Pascal disk for a project of mine, but there seem to be no modern tools capable of doing this. Pcode tool looked promising as it has an option to transfer data files but when I tried it the files were corrupted and of the wrong size. My thinking is to use the REM: device which is 300 baud by default, connect the PC to the TI RS232 via a straight serial cable, and use the Transfer command in the Filer to receive the file. The problem will be to figure out on the PC end how to send that file since all terminal emulators use some kind of file transfer protocol like Kermit or Zmodem. An alternative that might be work is to convert your binary files to Intel HEX format. Intel Hex Format (interlog.com) Intel HEX is a text version of a binary file. I used to use it to send binary data FROM embedded boards to the terminal in the past. I might be able to make a tool for TI-99 to convert a binary file to intel hex. Then you would need a converter in PASCAL to convert the other way. Edited January 17 by TheBF corrected text 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 17 Author Share Posted January 17 2 hours ago, senior_falcon said: It sounds like Pascal is using that workspace for other things besides to the usual interrupt routines. It's actually not besides but instead of. Pascal handles interrupts by polling the interrupt input as a part of the execution of the PME. The interrupts aren't triggered by the CPU's interrupt hardware, but by software polling. There are several reasons for this. One reason is sprite automotion. Pascal supports that, but in a different and more efficient way, specifically designed to fit Pascal's more advanced sprite handling. Another reason is DSR handling efficiency. Just like the p-system pre-searches DSR routines at boot time, finds the proper CRU and entry addresses for the routines it's interested in and stores them in a PCB (Peripheral Control Block, an ordinary PAB with some extra data) in order to quicker run the proper DSR once the system has started, it also pre-scans interrupt routines. Thus it knows in advance that the RS232 card has an interrupt routine. It creates a list of the CRU addresses of cards that do have an interrupt routine, so it doesn't waste time scanning through the entire range of CRU base addresses each time an interrupt occurs. A third reason is the p-system has the keyboard buffer I've discussed above. That's also running in the simulated interrupt routine. Since some of these things must occur with the p-code card disabled, as some interrupt service routines are on other cards, the code for this is in 8 K RAM, and parts of that runs with a workspace in 8 K RAM too. 3 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 17 Author Share Posted January 17 5 hours ago, Vorticon said: I wonder if BLOCKREAD would work since it does not care about the kind of data it receives. Set up an array of bytes as the receiving buffer and REM:datafile as the input file and set the block number to transfer to 12. I suspect timing would be an issue though... It does not look like a proper terminal program capable of binary file transfers has been made for UCSD Pascal that I can find anyway. Blockread will read blocks from blocked devices. The REMIN: and REMOUT: devices aren't blocked. They are handled one character at a time. There were terminal programs designed in the 1980's, like TEP, but I never bothered much getting any, since back then I didn't have anyone to communicate with. Everybody was further away, so exchange of files was via diskette transfer, not electronic communication. 1 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 17 Share Posted January 17 2 hours ago, TheBF said: An alternative that might be work is to convert your binary files to Intel HEX format. Intel Hex Format (interlog.com) Intel HEX is a text version of a binary file. I used to use it to send binary data FROM embedded boards to the terminal in the past. I might be able to make a tool for TI-99 to convert a binary file to intel hex. Then you would need a converter in PASCAL to convert the other way. I like that idea. I'll take a close look at that document and see if I can create a conversion program in Pascal. I'm sure there are many converters on the PC end of things so that should not be an issue. 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 17 Share Posted January 17 This picture from wikipedia tells nicely how to decode intelHEX. (I am gonna do one in Forth now for myself. ) I just read that it this goes back to 1973 so it fits well with our hobby. File example[edit] This example shows a file that has four data records followed by an end-of-file record: :10010000214601360121470136007EFE09D2190140 :100110002146017E17C20001FF5F16002148011928 :10012000194E79234623965778239EDA3F01B2CAA7 :100130003F0156702B5E712B722B732146013421C7 :00000001FF Start code Byte count Address Record type Data Checksum 2 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 18 Share Posted January 18 19 hours ago, apersson850 said: Blockread will read blocks from blocked devices. The REMIN: and REMOUT: devices aren't blocked. They are handled one character at a time. There were terminal programs designed in the 1980's, like TEP, but I never bothered much getting any, since back then I didn't have anyone to communicate with. Everybody was further away, so exchange of files was via diskette transfer, not electronic communication. UNITREAD should do the trick actually. Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 18 Author Share Posted January 18 Unitread can handle both blocked and character devices - that's correct. Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 18 Share Posted January 18 I'm trying to write a small assembly function to do a 2's complement conversion of an integer. Here's the code: .RELFUNC TWOCOMP,1 MOV *R10+,R1 ;GET FUNCTION ARGUMENT INV R1 ;DO 2'S COMPLEMENT CONVERSION INC R1 MOV R1,*R10 ;PLACE RESULT ON STACK B *R11 .END And here's a small test program: PROGRAM TWOCOMPTEST; VAR N : INTEGER; FUNCTION TWOCOMP(N : INTEGER):INTEGER; EXTERNAL; BEGIN PAGE(OUTPUT); WRITELN('ENTER NUMBER'); READLN(N); N := TWOCOMP(N); WRITELN('2 COMPLEMENT: ',N); END. It's giving me the wrong result. If for example you enter 19, you get -19 instead of the expected 237 and that's because the sign bit is being set by the operation and so the computer is displaying the 2' complement of 237 with a negative sign (-19). 19 = 0001 0011 2'complement is 1110 1101 = 237 Any way I can fix this? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 18 Share Posted January 18 (edited) 1 hour ago, Vorticon said: I'm trying to write a small assembly function to do a 2's complement conversion of an integer. Here's the code: .RELFUNC TWOCOMP,1 MOV *R10+,R1 ;GET FUNCTION ARGUMENT INV R1 ;DO 2'S COMPLEMENT CONVERSION INC R1 MOV R1,*R10 ;PLACE RESULT ON STACK B *R11 .END And here's a small test program: PROGRAM TWOCOMPTEST; VAR N : INTEGER; FUNCTION TWOCOMP(N : INTEGER):INTEGER; EXTERNAL; BEGIN PAGE(OUTPUT); WRITELN('ENTER NUMBER'); READLN(N); N := TWOCOMP(N); WRITELN('2 COMPLEMENT: ',N); END. It's giving me the wrong result. If for example you enter 19, you get -19 instead of the expected 237 and that's because the sign bit is being set by the operation and so the computer is displaying the 2' complement of 237 with a negative sign (-19). 19 = 0001 0011 2'complement is 1110 1101 = 237 Any way I can fix this? The two’s complement of 19 is, in fact, -19. With your ALC, you are dealing with words, so 19 = 0000 0000 0001 0011. The ones’ complement is 1111 1111 1110 1100 (-20) and the two’s complement is 1111 1111 1110 1101 (-19). You can reduce your two instructions to simply one NEG, which saves an instruction. If you are needing just the right byte (LSB), you can mask off the MSB, making your code .RELFUNC TWOCOMP,1 MOV *R10+,R1 ;GET FUNCTION ARGUMENT NEG R1 ;DO 2'S COMPLEMENT CONVERSION ANDI R1,>00FF ;mask off MSB MOV R1,*R10 ;PLACE RESULT ON STACK B *R11 .END ...lee Edited January 18 by Lee Stewart clarification 2 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 19 Share Posted January 19 That's where I was going wrong. Byte versus word. Works fine now. Thanks! 1 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 19 Share Posted January 19 I went ahead and created an Intel Hex format converter which takes a text file with Intel Hex data and converts it to a binary data file. Seems to work well. {INTEL HEX FORMAT CONVERSION PROGRAM CONVERTS FILE FROM HEX TO BINARY} PROGRAM HEXCONVERT; TYPE BYTE = 0..255; VAR CHECKSUM, DATACHECK, DATALEN, I, BYTEVAL : BYTE; INFILE : TEXT; OUTFILE : FILE OF BYTE; REC : STRING[255]; FNAME : STRING[20]; HEXBYTE : STRING[2]; {FUNCTION TO CONVERT NUMBER TO 2'S COMPLEMENT} FUNCTION TWOCOMP(N : INTEGER) : INTEGER; EXTERNAL; FUNCTION HEX2DEC(HVAL : STRING) : BYTE; {CONVERT HEX DIGIT PAIR TO INTEGER VALUE} VAR CH : CHAR; DVAL, VAL : BYTE; BEGIN VAL := 0; CH := HVAL[1]; IF (ORD(CH) > 47) AND (ORD(CH) < 58) THEN DVAL := (ORD(CH) - 48) * 16 ELSE DVAL := (ORD(CH) - 55) * 16; VAL := VAL + DVAL; CH := HVAL[2]; IF (ORD(CH) > 47) AND (ORD(CH) < 58) THEN DVAL := ORD(CH) - 48 ELSE DVAL := ORD(CH) - 55; VAL := VAL + DVAL; HEX2DEC := VAL; END; {HEX2DEC} BEGIN PAGE(OUTPUT); WRITELN('INPUT FILENAME? '); READLN(FNAME); RESET(INFILE,FNAME); WRITELN('OUTPUT FILENAME? '); READLN(FNAME); REWRITE(OUTFILE,FNAME); WHILE NOT EOF(INFILE) DO BEGIN {READ A RECORD} READLN(INFILE, REC); {EXTRACT THE DATA LENGTH} HEXBYTE := COPY(REC,2,2); DATALEN := HEX2DEC(HEXBYTE); IF DATALEN <> 0 THEN BEGIN DATACHECK := DATALEN; I := 10; {READ EACH HEX DIGIT PAIR AND CONVERT TO INTEGER} WHILE I <= (DATALEN + 8) DO BEGIN HEXBYTE := COPY(REC,I,2); BYTEVAL := HEX2DEC(HEXBYTE); DATACHECK := DATACHECK + BYTEVAL; OUTFILE^ := BYTEVAL; PUT(OUTFILE); I := I + 2; END; WRITELN; {COMPUTE RECORD CHECKSUM} DATACHECK := TWOCOMP(DATACHECK); {EXTRACT RECORD CHECKSUM} HEXBYTE := COPY(REC,10 + DATALEN,2); CHECKSUM := HEX2DEC(HEXBYTE); {COMPARE COMPUTED AND EXTRACTED CHECKSUMS. MUST MATCH} IF CHECKSUM <> DATACHECK THEN WRITELN('WARNING! CHECKSUM ERROR!'); END; END; CLOSE(INFILE); CLOSE(OUTFILE,LOCK); WRITELN; WRITELN('FILE CONVERSION DONE'); END. 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 19 Share Posted January 19 Cool! Typically this program would load the file into memory, so you might be able to make a simpler version that doesn't need an output file. It just reads the address field and puts each data word into sequential memory "cells". I had to travel today for business but I am doing a Forth version called HEXLOAD. 1 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted January 19 Share Posted January 19 2 hours ago, TheBF said: Cool! Typically this program would load the file into memory, so you might be able to make a simpler version that doesn't need an output file. It just reads the address field and puts each data word into sequential memory "cells". I had to travel today for business but I am doing a Forth version called HEXLOAD. Doing it on the fly is slower than having a binary data file ready to go where you just read and push the data to sequential memory locations. I'm thinking specifically of pattern and color picture files to be transferred to the VDP during a program run. I do seem to have an issue though. Upon inspection of the resulting binary file, it is writing 16-bit words for each hex digit pair instead of bytes even though I set the output file to be a file of bytes. As a test I used a hex format file as below which holds the digits 1 to 5 as data: :0A0000010102030405E7 :000000FF The resulting output file looks like this: >0001,>0002,>0003,>0004,>0005 I really need it to look like this: >0102,>0304,>0500 Any thoughts? Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 19 Author Share Posted January 19 (edited) The smallest normal storage unit is a word, i.e. 16 bits. Even a boolean is stored as 16 bits, and that happens for the file buffer too. A packed array[0..1] of byte will squeeze in two 8-bit pieces in one 16-bit word. When messing with values like this a type definition like this one comes in handy: type byte = 0..255; dual = record case boolean of false:(single: integer); true: (dual: packed array[0..1] of byte); end; (* record *) var buffer: dual; With that in place, you can use code like this: buffer.dual[0] := one_byte_val; buffer.dual[1] := another_byte_val; outfile^ := buffer.single; There two different byte-sized data pieces are stuffed into two different elements of a packed byte-sized array, which shares the same memory as one 16-bit integer. This type of handling is more efficient for combining two 8-bit values into one 16-bit (or the opposite) than using * (multiply), div or mod to accomplish the same thing. There are other applications for variant records too. One of the more conventional is to create a file type that allows for different content within the same memory space for different blocks in the file. If you have a data file, you can declare a record where one variant maps all the fields of whatever file header stuff you need. The other variant is just an array[0..255] of integer, or maybe a packed array[0.511] of char, or whatever you need. Then you write and read the first block in the file as the first type of record, the other blocks as the second. Makes it easy to map the file data areas. A more odd application is to define a record as being either an integer or a pointer to an integer. Then you can store any 16-bit value into your variable as an integer, then access the same variable as a pointer and read or write data to any word in memory. You create the functions peek and poke that way. A third use is as we did above, for data manipulation. The same dual type above can also be used to implement a Pascal version of SWPB (swap bytes). Finally, when handling variable data in the same type, don't forget the intrinsics moveleft, moveright and fillchar. They don't care about the type declaration and are fast for larger amounts of data. You can move data of any size and anywhere in memory, but the intrinsics will check to see if both source and destination are on even addresses and the number of bytes to move is an even number, then they'll use MOV instead of MOVB internally. Only half as many moves in that case. Now I do know you are learning how to combine assembly with Pascal, but UCSD Pascal does have support for a surprisingly large amount of things that can be done already at Pascal level, although it may not immediately be obvious. To do a 2-complement, then keep only the lower 8 bits, can be done like this: var n: integer; n := ord(odd(-n) and odd(255)); You can use boolean operators like and only on booleans. The operator odd has the odd(!) functionality of changing the type of the data to boolean but keep the data content of the integer as it was. Thus you can do boolean operations bitwise on integers directly in Pascal. The operator ord converts the type back to integer again, so you can assign the result to an integer. The output of ord(integer_value) is integer_value with type integer. Unlike if you do ord(character), where the type is changed from char to integer and returns the ordinal value of the character. If you expose 'A' to this treatment you get 65. Assembly's equivalen of NEG is - (minus sign). The equivalent of INV is not. So if you want to invert an integer, then keep the lower byte, you can do like this instead: var n: integer; n := ord(not(odd(n)) and odd(255)); This behavior is not according to standard Pascal! But it does work with UCSD Pascal. Edited January 19 by apersson850 3 Quote Link to comment Share on other sites More sharing options...
GDMike Posted January 19 Share Posted January 19 12 hours ago, Vorticon said: That's where I was going wrong. Byte versus word. Works fine now. Thanks! During my code reviews one of the things I have to always make sure of if am I doing a byte operation or a word op. Am I looking for a result as a byte or a word.. because if I don't come back and review that I usually get bit, not bited. 1 1 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.