After looking at the I/O routines I was troubled that while BYTE and STRING were covered adequately, CARD and INT were left out in the cold. The only built in routines for reading and writing CARDs and INTs are the Print and Input variants which are string based. Using them is further complicated as they need to be delimited if stored in a file.
And on top of being delimited, the storage consumes more bytes than necessary for the data type. CARDs and INTs, within the value ranges supported by Action! can be stored in 2 bytes. Using the Print routines a large CARD value will consume 5 bytes, and large negative INT will consume 6 bytes. That can add up to a lot of wasted space.
So I wrote my own routines:
- PutCD : Puts a CARD value to the specified device
- GetCD : Gets a CARD value from the specified device
- PutID : Puts an INT value to the specified device
- GetID : Gets an INT value from the specified device
PutCD has a dependency on PutD(). GetCD has a dependency on GetD(). Likewise PutID has a dependency on PutCD() and GetID has a dependency on GetCD().
I had some trouble getting PutCD to compute the MSB (most significant byte) of the CARD value using the division by 256 method where the MSB would not be correct within the last 213 values of the maximum CARD value. A member of AtariAge forums named fujidude offered an alternate method using bit shifting which works fine. The version presented here includes that method.
The syntax for these functions is as follows:
- PutCD(BYTE device CARD value)
- PutID(BYTE device INT value)
- CARD GetCD(BYTE device)
- INT GetID(BYTE device)
The Get routines return the read value as the appropriate data type (CARD or INT).
Routines
This is the source for my routines. I’m not going to break them down line by line, but instead have commented them inline.
PutCD
; PutCD procedure. Puts a CARD to a device. ; Requires a byte parameter which is the device channel to write to, ; and a card parameter which is the card to write. PROC PutCD(BYTE bDev CARD cVal) ; Declare bytes bL,bM for computing LSB,MSB and initialize to 0 BYTE bL=[0],bM=[0] ; Compute the MSB by right shifting 8 bits of the card ; effectively moving the MSB into the LSB. The LSB will ; be stored in the byte bM. bM=cVal RSH 8 ; Compute the LSB using Action! trickery. Assigning a card to ; a byte assigns the LSB of the card to the byte. bL=cVal ; Use the Put byte routine to put the LSB to the device, then ; the MSB to the device (standard 6502 format). PutD(bDev,bL) PutD(bDev,bM) RETURN
PutID
; PutID procedure. Puts an INT to a device. ; Requires a byte parameter which is the device channel to write to, ; and an int parameter which is the int to write. PROC PutID(BYTE bDev, INT iVal) ; Declare temporary card cTmp CARD cTmp ; If the INT iVal is less than 0 (negative), more work to do if iVal < 0 then ; Make int positive by multiplying by -1 cTmp=iVal * -1 ; Convert int to high value card by flipping all the bits ; with XOR and adding 1 cTmp=(cTmp XOR $FFFF) + 1 else ; If the INT iVal is positive, just assign it cTmp=iVal fi ; Put the computed card value to the device using the PutCD routine PutCD(bDev,cTmp) RETURN
GetCD
; GetCD function. Gets a CARD from device. ; Returns a CARD value. Requires one ; byte parameter which is the device channel to read from. CARD FUNC GetCD(BYTE bDev) ; Declare bytes bL,bM to hold computed LSB and MSB BYTE bL=[0],bM=[0] ; Declare card cV and initialize to 0 CARD cV=[0] ; Get the LSB byte from the device bL=GetD(bDev) ; Get the MSB byte from the device bM=GetD(bDev) ; Compute the card value by multiplying the ; MSB by 256, and then adding the LSB cV=(bM * 256) + bL ; Return the computed card value RETURN(cV)
GetID
; GetID function. Gets an INT from device. ; Returns an INT value. Requires one ; byte parameter which is the device channel to read from. INT FUNC GetID(BYTE bDev) ; Declare temporary card cTmp and initialize to 0 CARD cTmp=[0] ; Declare int iV and initialize to 0 INT iV=[0] ; Get a card from the device and store in cTmp using the GetCD routine cTmp=GetCD(bDev) ; If the card value is greater than 32768 ($8000) then ; the value is a negative number, so there is more work. if cTmp > $8000 then ; To convert the negative int stored in a card back ; to int, all the bits must be flipped (done through XOR) ; then add 1 to it. cTmp=(cTmp XOR $FFFF)+1 ; Now it needs to be made negative by multiplying by -1 iV=cTmp * -1 else ; If the card value was less than 32768, just assign it iV=cTmp fi ; Return the computed int value RETURN(iV)
Sample Program
I created a sample program to test the functions. It writes a byte value, a card value, a positive integer, and a negative integer to a file (without delimiters); resets the variables to zero; then reads them back in. It prints the values at each point they are changed (start, reset, post read).
I’m not going to break down the entire program this time. It is commented enough to understand it.
MODULE ; Declare global byte bgVal and assign initial value of 255 ; Declare global byte bgLSB,bgMSB for use later BYTE bgVal=[255],bgLSB,bgMSB ; Declare global card cgVal and assign initial value of 32768 CARD cgVal=[32768] ; Declare global int igPVal (pos), igNVal (neg) and ; assign initial value of 1 INT igPVal=[1],igNVal=[1] ; GetCD function. Gets a CARD from device. ; Returns a CARD value. Requires one ; byte parameter which is the device channel to read from. CARD FUNC GetCD(BYTE bDev) ; Declare bytes bL,bM to hold computed LSB and MSB BYTE bL=[0],bM=[0] ; Declare card cV and initialize to 0 CARD cV=[0] ; Get the LSB byte from the device bL=GetD(bDev) ; Get the MSB byte from the device bM=GetD(bDev) ; Compute the card value by multiplying the ; MSB by 256, and then adding the LSB cV=(bM * 256) + bL ; Return the computed card value RETURN(cV) ; GetID function. Gets an INT from device. ; Returns an INT value. Requires one ; byte parameter which is the device channel to read from. INT FUNC GetID(BYTE bDev) ; Declare temporary card cTmp and initialize to 0 CARD cTmp=[0] ; Declare int iV and initialize to 0 INT iV=[0] ; Get a card from the device and store in cTmp using the GetCD routine cTmp=GetCD(bDev) ; If the card value is greater than 32768 ($8000) then ; the value is a negative number, so there is more work. if cTmp > $8000 then ; To convert the negative int stored in a card back ; to int, all the bits must be flipped (done through XOR) ; then add 1 to it. cTmp=(cTmp XOR $FFFF)+1 ; Now it needs to be made negative by multiplying by -1 iV=cTmp * -1 else ; If the card value was less than 32768, just assign it iV=cTmp fi ; Return the computed int value RETURN(iV) ; PutCD procedure. Puts a CARD to a device. ; Requires a byte parameter which is the device channel to write to, ; and a card parameter which is the card to write. PROC PutCD(BYTE bDev CARD cVal) ; Declare bytes bL,bM for computing LSB,MSB and initialize to 0 BYTE bL=[0],bM=[0] ; Compute the MSB by right shifting 8 bits of the card ; effectively moving the MSB into the LSB. The LSB will ; be stored in the byte bM. bM=cVal RSH 8 ; Compute the LSB using Action! trickery. Assigning a card to ; a byte assigns the LSB of the card to the byte. bL=cVal ; Use the Put byte routine to put the LSB to the device, then ; the MSB to the device (standard 6502 format). PutD(bDev,bL) PutD(bDev,bM) RETURN ; PutID procedure. Puts an INT to a device. ; Requires a byte parameter which is the device channel to write to, ; and an int parameter which is the int to write. PROC PutID(BYTE bDev, INT iVal) ; Declare temporary card cTmp CARD cTmp ; If the INT iVal is less than 0 (negative), more work to do if iVal < 0 then ; Make int positive by multiplying by -1 cTmp=iVal * -1 ; Convert int to high value card by flipping all the bits ; with XOR and adding 1 cTmp=(cTmp XOR $FFFF) + 1 else ; If the INT iVal is positive, just assign it cTmp=iVal fi ; Put the computed card value to the device using the PutCD routine PutCD(bDev,cTmp) RETURN ; Procedure to print the values of all variables PROC PrVals() ; Print the global byte value bgVal with EOL PrintF("Byte: %B%E",bgVal) ; Print the global card value cgVal, along with the ; LSB and MSB of cgVal and an EOL PrintF("Card: %U (%B,%B)%E",cgVal,bgLSB,bgMSB) ; Print the positive int igPVal with EOL PrintF("Int+: %I%E",igPVal) ; Print the negative int igNVal with EOL PrintF("Int-: %I%E",igNVal) RETURN ; The main program routine PROC Main() ; Compute the global card cgVal MSB and LSB using the same ; method as PutCD. bgMSB=cgVal RSH 8 bgLSB=cgVal ; Initialize the negative int value since it was declared positive igNVal=igNVal * -1 ; Show the variables before starting PrintE("Initial Values") PrVals() ; Open the file TEST.DAT for writing Open(4,"D2:TEST.DAT",8,0) ; Put the byte value PutD(4,bgVal) ; Put the card value PutCD(4,cgVal) ; Put the positive int value PutID(4,igPVal) ; Put the negative int value PutID(4,igNVal) ; Close the file Close(4) ; Reset all the variables to 0, then print them PrintE("Reset Values") bgVal=0 bgLSB=0 bgMSB=0 cgVal=0 igNVal=0 igPVal=0 PrVals() ; Open the file TEST.DAT for reading Open(4,"D2:TEST.DAT",4,0) ; Get a byte from the device and store in bgVal bgVal=GetD(4) ; Get a card from the device and store in cgVal cgVal=GetCD(4) ; Get an int from the device and store in igPVal igPVal=GetID(4) ; Get an int from the device and store in igNVal igNVal=GetID(4) ; Close the device Close(4) ; Compute the LSB and MSB of global card cgVal again ; since we just read it from file. Again using the ; bit shift method. bgMSB=cgVal RSH 8 bgLSB=cgVal ; Print out the variables to confirm PrintE("Read Values") PrVals() ; Exit RETURN
Results
In these results, you can see some debug output (indented lines) I had in during PutCD troubleshooting that allowed me to see the values during each step of computation. The BYTE value started and ended at 255. The CARD value started and ended at 32768 with LSB of 0 and MSB of 128. The positive INT value started and ended at 1. The negative INT value started and ended at -1.
In this shot you can see the bytes written to the file TEST.DAT are indeed what they should be:
I had meant for this post to be something useful, and I suppose this still is, though not what I intended. And because bit shifting has now been touched on, one of the next posts will cover that.