1
;---------------------------------------------------------------------------------------------------------
2
; Source code converted by MSXBAS2ASM - MSX BASIC TO Z80 ASSEMBLY CONVERTER
3
; MSXBAS2ASM developed by Amaury Carvalho, 2019, Brazil
4
; http://launchpad.net/msxbas2asm
5
;---------------------------------------------------------------------------------------------------------
7
;--------------------------------------------------------
8
; MSX BIOS DATA/FUNCTION POINTERS
9
;--------------------------------------------------------
11
;---------------------------------------------------------------------------------------------------------
13
;---------------------------------------------------------------------------------------------------------
15
BIOS_CALBAS: equ 0x0159
16
BIOS_OUTDO: equ 0x0018 ; output to current device (i.e. screen)
17
BIOS_CHPUT: equ 0x00A2
19
BIOS_POSIT: equ 0x00C6
21
BIOS_CHGET: equ 0x009F
22
BIOS_CHSNS: equ 0x009C
23
BIOS_INLIN: equ 0x00B1
24
BIOS_PINLIN: equ 0x00AE
25
BIOS_QINLIN: equ 0x00B4
26
BIOS_GTSTCK: equ 0x00D5
27
BIOS_GTTRIG: equ 0x00D8
28
BIOS_GTPAD: equ 0x00DB
29
BIOS_GTPDL: equ 0x00DE
30
BIOS_DISSCR: equ 0x0041
31
BIOS_ENASCR: equ 0x0044
32
BIOS_CHGMOD: equ 0x005F
33
BIOS_CHGCLR: equ 0x0062
34
BIOS_CLRSPR: equ 0x0069
35
BIOS_INITXT: equ 0x006C ; init text mode 40 columns
36
BIOS_INIT32: equ 0x006F ; init text mode 32 columns
37
BIOS_INIGRP: equ 0x0072
38
BIOS_INIMLT: equ 0x0075
39
BIOS_SETTXT: equ 0x0078 ; set text mode 40 columns
40
BIOS_SETT32: equ 0x007B ; set text mode 32 columns
41
BIOS_SETGRP: equ 0x007E
42
BIOS_SETMLT: equ 0x0081
43
BIOS_CALPAT: equ 0x0084
44
BIOS_CALATR: equ 0x0087
45
BIOS_GSPSIZ: equ 0x008A
46
BIOS_GRPPRT: equ 0x008D
47
BIOS_ERAFNK: equ 0x00CC
48
BIOS_DSPFNK: equ 0x00CF
49
BIOS_TOTEXT: equ 0x00D2
50
BIOS_BREAKX: equ 0x00B7
51
BIOS_ISCNTC: equ 0x03FB
52
BIOS_CHKRAM: equ 0x0000
53
BIOS_GICINI: equ 0x0090
54
BIOS_WRTPSG: equ 0x0093
55
BIOS_REDPSG: equ 0x0096
56
BIOS_STRTMS: equ 0x0099
57
BIOS_KEYINT: equ 0x0038
58
BIOS_CALSLT: equ 0x001C
59
BIOS_ENASLT: equ 0x0024
60
BIOS_RSLREG: equ 0x0138
61
BIOS_SCALXY: equ 0x010E
62
BIOS_MAPXYC: equ 0x0111 ; in BC = X, DE = Y
63
BIOS_READC: equ 0x011D ; out A = color of current pixel
64
BIOS_SETATR: equ 0x011A ; in A = color code
65
BIOS_SETC: equ 0x0120 ; set current pixel to color from SETATR
66
BIOS_NSETCX: equ 0x0123 ; in HL = pixel fill count
67
BIOS_SCANR: equ 0x012C ; in B=Fill switch, DE=Skip count, out DE=Skip remainder, HL=Pixel count
68
BIOS_SCANL: equ 0x012F ; out HL=Pixel count
69
BIOS_FETCHC: equ 0x0114 ; out A = cursor mask, HL = VRAM address of cursor
70
BIOS_STOREC: equ 0x0117 ; in A = cursor mask, HL = VRAM address of cursor
71
BIOS_RESET: equ 0x7D17 ; restart BASIC
72
BIOS_IOALLOC: equ 0X7e6b ; memory setup
74
BIOS_GETVCP: equ 0x0150 ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
75
BIOS_GETVC2: equ 0x0153 ; get PSG voice buffer address (VOICEN = voice number, in L = byte number 0-36, out HL = address)
77
BIOS_CHPUT_LF: equ 0x0908
78
BIOS_CHPUT_CR: equ 0x0A81
79
BIOS_CHPUT_TAB: equ 0x0A71
82
BIOS_CHKNEW: equ 0x0165 ; C-flag set if screenmode = 5, 6, 7 or 8
83
BIOS_EXTROM: equ 0x015F
84
BIOS_SCALXY2: equ 0x008D ; in BC = X, DE = Y
85
BIOS_MAPXYC2: equ 0x0091 ; in BC = X, DE = Y
86
BIOS_SETC2: equ 0x009D ; set current pixel to color from SETATR
87
BIOS_READC2: equ 0x0095 ; out A = color of current pixel
88
BIOS_CHGMOD2: equ 0x00D1 ; in A = screenmode
89
BIOS_DOBOXF: equ 0x0079 ; hl = basic text pointer
90
BIOS_GRPPRT2: equ 0x0089 ; a = character
91
BIOS_CHGCLR2: equ 0x0111 ; change color, a = screen mode
92
BIOS_CALPAT2: equ 0x00F9
93
BIOS_CALATR2: equ 0x00FD
94
BIOS_GSPSIZ2: equ 0x0101
95
BIOS_CLRSPR2: equ 0x00F5
97
;---------------------------------------------------------------------------------------------------------
99
;---------------------------------------------------------------------------------------------------------
101
BIOS_VERSION: equ 0x002D ; 0 = MSX1, 1 = MSX2, 2 = MSX2+, 3 = MSXturboR
102
BIOS_FORCLR: equ 0xF3E9
103
BIOS_BAKCLR: equ 0xF3EA
104
BIOS_BDRCLR: equ 0xF3EB
105
BIOS_ATRBYT: equ 0xF3F2
106
BIOS_INTFLG: equ 0xFC9B
107
BIOS_EXPTBL: equ 0xFCC1
108
BIOS_JIFFY: equ 0xFC9E
109
BIOS_BOTTOM: equ 0xFC48
110
BIOS_HIMEM: equ 0xFC4A
111
BIOS_SCRMOD: equ 0xFCAF ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode.
112
BIOS_CLIKSW: equ 0xF3DB ; 0=keyboard click off, 1=keyboard click on
113
BIOS_GRPACX: equ 0xFCB7
114
BIOS_GRPACY: equ 0xFCB9
115
BIOS_DATLIN: equ 0xF6A3 ; 2 - line number of DATA statement read by READ statement
116
BIOS_DATPTR: equ 0xF6C8 ; 2 - address of data read by executing READ statement
117
BIOS_FLGINP: equ 0xF6A6 ; 1 - flag used in INPUT or READ
118
BIOS_TEMP: equ 0xF6A7 ; 2
119
BIOS_TEMP2: equ 0xF6BC ; 2
120
BIOS_TEMP3: equ 0xF69D ; 2
121
BIOS_TEMP8: equ 0xF69F ; 2
122
BIOS_TEMP9: equ 0xF7B8 ; 2
123
BIOS_OLDSCR: equ 0xFCB0 ; screen mode of the last text mode set
124
BIOS_LINL40: equ 0xF3AE ; width for 40 columns screen mode
125
BIOS_LINL32: equ 0xF3AF ; width for 32 columns screen mode
126
BIOS_LINLEN: equ 0xF3B0 ; current width for text screen mode
127
BIOS_CLMLST: equ 0xF3B2 ; minimum number of columns that must still be available on a line for a CRLF
128
BIOS_TXTNAM: equ 0xF3B3 ; characters table name
130
BIOS_VOICEN: equ 0xFB38 ; PSG voice number
131
BIOS_MCLTAB: equ 0xF956
132
BIOS_PRSCNT: equ 0xFB35
133
BIOS_SAVSP: equ 0xFB36
134
BIOS_QUEUEN: equ 0xFB3E
135
BIOS_MUSICF: equ 0xFB3F ;contains 3 bit flags set by the STRTMS. Bits 0, 1 and 2 correspond to VOICAQ, VOICBQ and VOICCQ.
136
BIOS_PLYCNT: equ 0xFB40
138
BIOS_DRWFLG: equ 0xFCBB
139
BIOS_MCLFLG: equ 0xF958
141
BIOS_SLTROM: equ 0xFCC1
142
BIOS_RAMAD0: equ 0xF341 ; Main-RAM Slot (00000h~03FFFh)
143
BIOS_RAMAD1: equ 0xF342 ; Main-RAM Slot (04000h~07FFFh)
144
BIOS_RAMAD2: equ 0xF343 ; Main-RAM Slot (08000h~0BFFFh)
145
BIOS_RAMAD3: equ 0xF344 ; Main-RAM Slot (0C000h~0FFFFh)
149
;--------------------------------------------------------
150
; MSX BASIC DATA/FUNCTION POINTERS
151
;--------------------------------------------------------
153
;---------------------------------------------------------------------------------------------------------
154
; MSX BASIC FUNCTIONS
155
;---------------------------------------------------------------------------------------------------------
157
BASIC_AUTO: equ 0x3973
158
BASIC_AND: equ 0x3A18
159
BASIC_ATTR: equ 0x39FE
160
BASIC_BASE: equ 0x39BE
161
BASIC_BSAVE: equ 0x39CC
162
BASIC_BLOAD: equ 0x39CA
163
BASIC_BEEP: equ 0x39AC
164
BASIC_CALL: equ 0x39C0
165
BASIC_CLOSE: equ 0x3994
166
BASIC_COPY: equ 0x39D8
167
BASIC_CONT: equ 0x395E
168
BASIC_CLEAR: equ 0x3950
169
BASIC_CLOAD: equ 0x3962
170
BASIC_CSAVE: equ 0x3960
171
BASIC_CSRLIN: equ 0x39FC
172
BASIC_CIRCLE: equ 0x39A4
173
BASIC_COLOR: equ 0x39A6
174
BASIC_CLS: equ 0x396A
175
BASIC_CMD: equ 0x39DA
176
BASIC_DELETE: equ 0x397C
177
BASIC_DATA: equ 0x3934
178
BASIC_DIM: equ 0x3938
179
BASIC_DEFSTR: equ 0x3982
180
BASIC_DEFINT: equ 0x3984
181
BASIC_DEFSNG: equ 0x3986
182
BASIC_DEFDBL: equ 0x3988
183
BASIC_DSKO: equ 0x39CE
184
BASIC_DEF: equ 0x395A
185
BASIC_DSKI: equ 0x3A00
186
BASIC_DRAW: equ 0x39A8
187
BASIC_ELSE: equ 0x396E
188
BASIC_END: equ 0x392E
189
BASIC_ERASE: equ 0x3976
190
BASIC_ERROR: equ 0x3978
191
BASIC_ERL: equ 0x39EE
192
BASIC_ERR: equ 0x39F0
193
BASIC_EQU: equ 0x3A1E
194
BASIC_FOR: equ 0x3920
195
BASIC_FIELD: equ 0x398E
196
BASIC_FILES: equ 0x39AA
198
BASIC_GOTO: equ 0x393E
199
BASIC_GOSUB: equ 0x3948
200
BASIC_GET: equ 0x3990
201
BASIC_INPUT: equ 0x3936
203
BASIC_INSTR: equ 0x39F6
204
BASIC_IMP: equ 0x3A20
205
BASIC_INKEY: equ 0x3A04
206
BASIC_IPL: equ 0x39D6
207
BASIC_KILL: equ 0x39D4
208
BASIC_KEY: equ 0x3964
209
BASIC_LPRINT: equ 0x394C
210
BASIC_LLIST: equ 0x3968
211
BASIC_LET: equ 0x393C
212
BASIC_LOCATE: equ 0x39DC
213
BASIC_LINE: equ 0x398A
214
BASIC_LOAD: equ 0x3996
215
BASIC_LSET: equ 0x399C
216
BASIC_LIST: equ 0x3952
217
BASIC_LFILES: equ 0x39A2
218
BASIC_MOTOR: equ 0x39C8
219
BASIC_MERGE: equ 0x3998
220
BASIC_MOD: equ 0x3A22
221
BASIC_MAX: equ 0x39C6
222
BASIC_NEXT: equ 0x3932
223
BASIC_NAME: equ 0x39D2
224
BASIC_NEW: equ 0x3954
225
BASIC_NOT: equ 0x39EC
226
BASIC_OPEN: equ 0x398C
227
BASIC_OUT: equ 0x3964
230
BASIC_OFF: equ 0x3A02
231
BASIC_PRINT: equ 0x394E
232
BASIC_PUT: equ 0x3992
233
BASIC_POKE: equ 0x395C
234
BASIC_PSET: equ 0x39B0
235
BASIC_PRESET: equ 0x39B2
236
BASIC_POINT: equ 0x3A06
237
BASIC_PAINT: equ 0x39AA
238
BASIC_PLAY: equ 0x39AE
239
BASIC_RETURN: equ 0x3948
240
BASIC_READ: equ 0x393A
241
BASIC_RUN: equ 0x3940
242
BASIC_RESTORE:equ 0x3944
243
BASIC_REM: equ 0x394A
244
BASIC_RESUME: equ 0x397A
245
BASIC_RSET: equ 0x399E
246
BASIC_RENUM: equ 0x3980
247
BASIC_SCREEN: equ 0x39B6
248
BASIC_SPRITE: equ 0x39BA
249
BASIC_STOP: equ 0x394C
250
BASIC_SWAP: equ 0x3974
251
BASIC_SET: equ 0x39D0
252
BASIC_SAVE: equ 0x39A0
253
BASIC_SPC: equ 0x39EA
254
BASIC_STEP: equ 0x39E4
255
BASIC_STRING: equ 0x39F2
256
BASIC_SPACE1: equ 0x397E
257
BASIC_SOUND: equ 0x39B4
258
BASIC_THEN: equ 0x39E0
259
BASIC_TRON: equ 0x3970
260
BASIC_TROFF: equ 0x3972
261
BASIC_TAB: equ 0x39E2
263
BASIC_TIME: equ 0x39C2
264
BASIC_USING: equ 0x39F4
265
BASIC_USR: equ 0x39E6
266
BASIC_VARPTR: equ 0x39FA
267
BASIC_VDP: equ 0x39BC
268
BASIC_VPOKE: equ 0x39B8
269
BASIC_WIDTH: equ 0x396C
270
BASIC_WAIT: equ 0x3958
271
BASIC_XOR: equ 0x3A1C
272
BASIC_ABS: equ 0x39E8
273
BASIC_ATN: equ 0x39F8
274
BASIC_ASC: equ 0x3A06
275
BASIC_BIN: equ 0x3A16
276
BASIC_CINT: equ 0x3A18
277
BASIC_CSNG: equ 0x3A1A
278
BASIC_CDBL: equ 0x3A1C
279
BASIC_CVI: equ 0x3A2C
280
BASIC_CVS: equ 0x3A2E
281
BASIC_CVD: equ 0x3A30
282
BASIC_COS: equ 0x39F4
283
BASIC_CHR: equ 0x3A08
284
BASIC_DSKF: equ 0x3A28
285
BASIC_EXP: equ 0x39F2
286
BASIC_EOF: equ 0x3A32
287
BASIC_FRE: equ 0x39FA
288
BASIC_FIX: equ 0x3A1E
289
BASIC_FPOS: equ 0x3A2A
290
BASIC_HEX: equ 0x3A12
291
BASIC_INT: equ 0x39E6
292
BASIC_INP: equ 0x39FC
293
BASIC_LPOS: equ 0x3A14
294
BASIC_LOG: equ 0x39F0
295
BASIC_LOC: equ 0x3A34
296
BASIC_LEN: equ 0x3A00
297
BASIC_LEFT: equ 0x39DE
298
BASIC_LOF: equ 0x3A36
299
BASIC_MKI: equ 0x3A38
300
BASIC_MKS: equ 0x3A3A
301
BASIC_MKD: equ 0x3A3C
302
BASIC_MID: equ 0x39E2
303
BASIC_OCT: equ 0x3A10
304
BASIC_POS: equ 0x39FE
305
BASIC_PEEK: equ 0x3A0A
306
BASIC_PDL: equ 0x3A24
307
BASIC_PAD: equ 0x3A26
308
BASIC_RIGHT: equ 0x39E0
309
BASIC_RND: equ 0x39EC
310
BASIC_SGN: equ 0x39E4
311
BASIC_SQR: equ 0x39EA
312
BASIC_SIN: equ 0x39EE
313
BASIC_STR: equ 0x3A02
314
BASIC_SPACE2: equ 0x3A0E
315
BASIC_STICK: equ 0x3A20
316
BASIC_STRIG: equ 0x3A22
317
BASIC_TAN: equ 0x39F6
318
BASIC_VAL: equ 0x3A04
319
BASIC_VPEEK: equ 0x3A0C
321
BASIC_TRAP_ENABLE: equ 0x631B ; ON INTERVAL/KEY/SPRITE/STOP/STRIG - hl = pointer to trap block
322
BASIC_TRAP_DISABLE: equ 0x632B ; hl = pointer to trap block
323
BASIC_TRAP_ACKNW: equ 0x6358 ; hl, acknowledge trap (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
324
BASIC_TRAP_PAUSE: equ 0x6331 ; hl
325
BASIC_TRAP_UNPAUSE: equ 0x633E ; hl
326
BASIC_TRAP_CLEAR: equ 0x636E
328
BASIC_PLAY_DIRECT: equ 0x744C
329
BASIC_DRAW_DIRECT: equ 0x568C
331
BASIC_READYR: equ 0x409B
332
BASIC_READYC: equ 0x7D17
333
BASIC_FACEVAL: equ 0x4DC7
335
BASIC_ERROR_HANDLER:equ 0x406F
336
BASIC_ERROR_SYNTAX: equ 0x4055
337
BASIC_ERROR_DIVZER: equ 0x4058
338
BASIC_ERROR_OVRFLW: equ 0x4067
339
BASIC_ERROR_ARRAY: equ 0x405E
340
BASIC_ERROR_TYPMIS: equ 0x406D
342
; BASIC ERROR CODES TO BASIC_ERROR_HANDLER
343
; 01 NEXT without FOR 19 Device I/O error
344
; 02 Syntax error 20 Verify error
345
; 03 RETURN without GOSUB 21 No RESUME
346
; 04 Out of DATA 22 RESUME without error
347
; 05 Illegal function call 23 Unprintable error
348
; 06 Overflow 24 Missing operand
349
; 07 Out of memory 25 Line buffer overflow
350
; 08 Undefined line number 50 FIELD overflow
351
; 09 Subscript out of range 51 Internal error
352
; 10 Redimensioned array 52 Bad file number
353
; 11 Division by zero 53 File not found
354
; 12 Illegal direct 54 File already open
355
; 13 Type mismatch 55 Input past end
356
; 14 Out of string space 56 Bad file name
357
; 15 String too long 57 Direct statement in file
358
; 16 String formula too complex 58 Sequential I/O only
359
; 17 Can't CONTINUE 59 File not OPEN
360
; 18 Undefined user function
362
;---------------------------------------------------------------------------------------------------------
363
; MSX BASIC WORK AREAS
364
;---------------------------------------------------------------------------------------------------------
366
BASIC_DAC: equ 0xF7F6 ; 16
367
BASIC_ARG: equ 0xF847 ; 16
368
BASIC_VALTYP: equ 0xF663
369
BASIC_RNDX: equ 0xF857
370
BASIC_BUF: equ 0xF55E ; 259
371
BASIC_KBUF: equ 0xF41F ; 318
372
BASIC_SWPTMP: equ 0xF7BC ; 8
373
BASIC_STRBUF: equ 0xF7C5 ; 43
374
BASIC_TXTTAB: equ 0xF676
375
BASIC_VARTAB: equ 0xF6C2
376
BASIC_ARYTAB: equ 0xF6C4
377
BASIC_STREND: equ 0xF6C6
378
BASIC_STKTOP: equ 0xF674
379
BASIC_FRETOP: equ 0xF69B
380
BASIC_MEMSIZ: equ 0xF672
382
BASIC_TEMPPT: equ 0xF678 ; 2 Starting address of unused area of temporary descriptor.
383
BASIC_TEMPST: equ 0xF67A ; 30 Temporary descriptors.
385
BASIC_DATPTR: equ 0xF6C8 ; 2 Pointer to next data to read from the instruction DATA. Modified by RESTORE.
386
BASIC_DATLIN: equ 0xF6A3 ; 2 Número de linha do comando DATA para o comando READ.
387
BASIC_DORES: equ 0xF664 ; 1 Usada pelo comando DATA para manter o texto no formato ASCII.
388
BASIC_DEFTBL: equ 0xF6CA ; 26 table of variables defined by DEFINT, DEFSTR, DEFSNG and DEFDBL for each alphabet letter (2 = integer, 3 = String, 4 = Simple precision, 8 = Double precision).
390
BASIC_CURLIN: equ 0xF41C ; BASIC current line number
391
BASIC_INTVAL: equ 0xFCA0 ; interval value
392
BASIC_INTCNT: equ 0xFCA2 ; interval current count
394
BASIC_PRMPRV: equ 0xF74C ; Pointer to previous parameter block in PARM1
396
BASIC_TRPTBL: equ 0xFC4C ; 78 trap table - array of 3 bytes - state[1] (bit 0=on, bit 1=stop, bit 2=active) + address[2]
398
BASIC_TRPTBL_KEY: equ 0xFC4C ; 30 ON KEY GOSUB
399
BASIC_TRPTBL_STOP: equ 0xFC6A ; 3 ON STOP GOSUB
400
BASIC_TRPTBL_SPRITE: equ 0xFC6D ; 3 ON SPRITE GOSUB
401
BASIC_TRPTBL_STRIG: equ 0xFC70 ; 15 ON STRIG GOSUB
402
BASIC_TRPTBL_INTERVAL: equ 0xFC7F ; 3 ON INTERVAL GOSUB
403
BASIC_TRPTBL_OTHER: equ 0xFC82 ; 24 reserved for expansion
405
BASIC_ONGSBF: equ 0xFBD8 ; 1 trap occurred counter (0=not occurred)
409
;--------------------------------------------------------
411
;--------------------------------------------------------
413
;--------------------------------------------------------
415
;--------------------------------------------------------
416
COMPILE_TO_ROM: EQU 1
418
MACRO __call_basic,CALL_PARM
423
if defined COMPILE_TO_DOS
425
MACRO __call_bios,CALL_PARM
426
;ld iy,(BIOS_EXPTBL-1)
428
call BIOS_CALBAS ; BIOS_CALSLT
433
MACRO __call_bios,CALL_PARM
440
push hl ; save parameter
444
pop iy ; restore PC of caller
445
pop hl ; get next parameter
446
push iy ; save PC of caller
450
pop iy ; restore PC of caller
451
push hl ; save return parameter
452
push iy ; save PC of caller
456
pop iy ; restore PC of caller
457
push hl ; save return parameter
458
push iy ; save PC of caller
462
MACRO set.line.number, line_number
463
ld bc, line_number ; current line number
464
ld (BASIC_CURLIN), bc
468
ld a, (BIOS_INTFLG) ; verify CTRL+BREAK
474
ld a, (BASIC_ONGSBF) ; trap occured counter
480
;---------------------------------------------------------------------------------------------------------
482
;---------------------------------------------------------------------------------------------------------
484
romSize: equ 0x8000 ; ROM size (32k)
485
pageSize: equ 0x4000 ; Page size (16k)
486
lowLimitSize: equ 0x400 ; 10% of a page size
488
if defined COMPILE_TO_BIN
490
pgmArea: equ 0x8000 ; page 2 - program area
491
ramArea: equ 0xc000 ; page 3 - free RAM start area
493
org pgmArea ; program binary type start address
494
db 0FEh ; binary file ID
495
dw start_pgm ; begin address
496
dw end_file - 1 ; end address
497
dw start_pgm ; program execution address (for ,R option)
500
if defined COMPILE_TO_ROM
502
pgmArea: equ 0x4000 ; page 1 and 2 - program area
503
ramArea: equ 0xc000 ; page 3 - free RAM start area
505
org pgmArea ; program rom type start address
506
db 'AB' ; rom file ID
508
dw 0x0000 ; STATEMENT
515
pgmArea: equ 0x8000 ; page 2 - program area
516
ramArea: equ 0xc000 ; page 3 - free RAM start area
518
org pgmArea ; program DOS type start address ; 0x0100
524
;---------------------------------------------------------------------------------------------------------
526
;---------------------------------------------------------------------------------------------------------
528
if defined COMPILE_TO_ROM
531
call PROGRAM_SLOT_2_RESTORE
532
__call_basic BASIC_READYR ; warm start Basic
537
call PROGRAM_SLOT_GET
538
ld (BIOS_RAMAD2), a ; Save RAM slot of page 8000h-BFFFh
541
PROGRAM_SLOT_2_RESTORE:
544
jp BIOS_ENASLT ; Select the RAM on page 8000h-BFFFh
546
PROGRAM_SLOT_2_ENABLE:
548
call PROGRAM_SLOT_ENABLE_SUB
550
jp BIOS_ENASLT ; Select the ROM on page 8000h-BFFFh
552
PROGRAM_SLOT_1_ENABLE:
556
call PROGRAM_SLOT_ENABLE_SUB
558
jp BIOS_ENASLT ; Select the ROM on page 4000h-7FFFh
560
PROGRAM_SLOT_ENABLE_SUB:
563
and 3 ;Keep bits corresponding to the page
582
; a <- slot ID formatted FxxxSSPP
583
; Modifies: af, bc, de, hl
584
; ref: https://www.msx.org/forum/msx-talk/development/fusion-c-and-htimi#comment-366469
588
jr z,PrimaryShiftContinue
593
PrimaryShiftContinue:
595
jr z,PrimaryShiftDone
610
inc hl ; move to SLTTBL
617
jr z,SecondaryShiftContinue
622
SecondaryShiftContinue:
624
jr nz,SecondaryShiftDone
634
if defined COMPILE_TO_DOS
639
__call_bios BIOS_ENASLT ; Select main ROM on page 0 (0000h~3FFFh)
645
__call_bios BIOS_ENASLT ; Select main ROM on page 1 (4000h~7FFFh)
652
;---------------------------------------------------------------------------------------------------------
654
;---------------------------------------------------------------------------------------------------------
656
start_pgm: ; start of the program
658
if defined COMPILE_TO_DOS
660
call BIOS_SLOT_ENABLE ; enable bios on page 0
661
call BASIC_SLOT_ENABLE ; enable basic on page 1
664
if defined COMPILE_TO_ROM
666
call PROGRAM_SLOT_2_SAVE ; save slot on page 2
667
call PROGRAM_SLOT_2_ENABLE ; enable program on page 2
672
__call_bios BIOS_ERAFNK ; turn off function keys display
673
__call_bios BIOS_GICINI ; initialize sound system
674
__call_bios BIOS_INITXT ; initialize text screen
676
ld (BIOS_CLIKSW), a ; disable keyboard click
678
ld (BASIC_CURLIN), bc ; interpreter in direct mode
679
__call_basic BASIC_TRAP_CLEAR ; clear traps work space
680
;call INITIALIZE_PARAMETERS ; initialize parameters stack
681
call memory.init ; initialize memory allocation
682
call INITIALIZE_VARIABLES ; initialize variables
686
set.line.number 10 ; current line number
689
set.line.number 20 ; current line number
692
set.line.number 30 ; current line number
693
call CLS ; action call
696
set.line.number 40 ; current line number
697
ld hl, LIT_5 ; parameter
699
ld hl, IDF_4 ; parameter
701
call LET ; action call
704
set.line.number 50 ; current line number
705
ld hl, LIT_8 ; parameter
707
call MATH.NEG ; action call
708
ld hl, IDF_6 ; parameter
710
call LET ; action call
713
set.line.number 60 ; current line number
714
ld hl, LIT_11 ; parameter
716
ld hl, IDF_10 ; parameter
718
call LET ; action call
721
set.line.number 70 ; current line number
722
ld hl, IDF_4 ; parameter
724
ld hl, LIT_13 ; parameter
726
call MATH.MULT ; action call
727
ld hl, LIT_15 ; parameter
729
call MATH.ADD ; action call
730
ld hl, IDF_12 ; parameter
732
call LET ; action call
735
set.line.number 80 ; current line number
736
ld hl, IDF_10 ; parameter
738
ld hl, IDF_6 ; parameter
740
ld hl, IDF_12 ; parameter
742
call MATH.DIV ; action call
743
call MATH.ADD ; action call
744
ld hl, IDF_10 ; parameter
746
call LET ; action call
749
set.line.number 90 ; current line number
750
ld hl, IDF_6 ; parameter
752
ld hl, LIT_18 ; parameter
754
call MATH.NEG ; action call
755
call MATH.MULT ; action call
756
ld hl, IDF_6 ; parameter
758
call LET ; action call
761
set.line.number 100 ; current line number
762
ld hl, IDF_4 ; parameter
764
ld hl, LIT_19 ; parameter
766
call MATH.ADD ; action call
767
ld hl, IDF_4 ; parameter
769
call LET ; action call
772
set.line.number 110 ; current line number
773
ld hl, IDF_10 ; parameter
775
ld hl, LIT_21 ; parameter
777
call MATH.MULT ; action call
778
ld hl, IDF_20 ; parameter
780
call LET ; action call
783
set.line.number 120 ; current line number
784
ld hl, IDF_20 ; parameter
786
ld hl, LIT_23 ; parameter
788
call MATH.SUB ; action call
789
ld hl, IDF_22 ; parameter
791
call LET ; action call
794
set.line.number 130 ; current line number
795
ld hl, IDF_20 ; parameter
797
call PRINT ; action call
798
ld hl, LIT_26 ; parameter
800
call PRINT ; action call
801
ld hl, IDF_22 ; parameter
803
call PRINT ; action call
804
ld hl, PRINT.CRLF ; parameter
806
call PRINT ; action call
809
set.line.number 140 ; current line number
812
;---------------------------------------------------------------------------------------------------------
814
;---------------------------------------------------------------------------------------------------------
816
end_pgm: __call_bios BIOS_DSPFNK ; turn on function keys display
818
ld (BIOS_CLIKSW), a ; enable keyboard click
820
if defined COMPILE_TO_ROM
823
__call_basic BASIC_READYR ; warm start Basic
826
ret ; end of the program
828
;__call_bios BIOS_GICINI ; initialize sound system
829
;if defined COMPILE_TO_DOS or defined COMPILE_TO_ROM
830
; __call_bios BIOS_RESET ; restart Basic
832
; __call_basic BASIC_END ; end to Basic
836
;---------------------------------------------------------------------------------------------------------
838
;---------------------------------------------------------------------------------------------------------
843
; out IX = variable assigned address
844
pop.parm ; get variable address parameter
845
push hl ; just to transfer hl to ix
847
ld a, (ix) ; get variable type
848
cp 3 ; test if string
849
jr nz, LET.PARM ; if not a string, it isn't necessary to free memory
850
ld a, (ix + 3) ; get variable string length
852
jr z, LET.PARM ; if zero, it isn't necessary to free memory
853
ld c, (ix + 4) ; get old string address low
854
ld b, (ix + 5) ; get old string address high
855
push ix ; save variable address
856
push bc ; just to transfer bc (old string address) to ix
858
call memory.free ; free memory
859
pop ix ; restore variable address
860
LET.PARM: pop.parm ; get data address parameter (out hl = data address)
861
ld a, (ix + 2) ; get variable type flag
862
or a ; cp 0 - test type flag (0=any, 255=fixed)
863
jr nz, LET.FIXED ; if type flag is fixed, so casting is necessary
864
LET.ANY: push ix ; just to transfer ix (variable address) to de
866
ldi ; copy 1 byte from hl (data address) to de (variable address)
867
inc de ; go to variable data area
869
inc hl ; go to data data area
871
ld bc, 8 ; data = 8 bytes
872
ldir ; copy bc bytes from hl (data address) to de (variable address)
873
ld a, (ix) ; get variable type
874
cp 3 ; test if string
875
ret nz ; if not string, return
876
jp LET.STRING ; else do string treatment (in ix = variable address)
877
LET.FIXED: push ix ; save variable destination address
878
push hl ; save variable source address
879
ld a, (ix) ; get variable fixed type, and hl has parameter data address
880
call CAST_TO ; cast data to type (in hl = variable address, a = type to, out hl = casted data address)
882
pop ix ; restore variable address
883
ld a, (ix) ; get variable destination type again
884
cp 3 ; test if string
885
jr nz, LET.VALUE ; if not string, do value treatment
886
ld a, (de) ; get variable source type again
887
cp 3 ; test if string
888
jr nz, LET.FIX1 ; if not string, get casted string size
893
ld (ix + 3), a ; source string size
896
call GET_STR.LENGTH ; get string length (in HL, out B)
898
ld (ix + 3), b ; set variable length
899
LET.FIX2: ld (ix + 4), l ; casted data address low
900
ld (ix + 5), h ; casted data address high
901
jp LET.STRING ; do string treatment (in ix = variable address)
902
LET.VALUE: push ix ; just to transfer ix (variable address) to de
904
inc de ; go to variable data area (and the data from its casted)
907
ld bc, 8 ; data = 8 bytes
908
ldir ; copy bc bytes from hl (data address) to de (variable address)
910
LET.STRING: ld a, (ix + 3) ; string size
911
or a ; cp 0 - test if null
912
jr nz, LET.ALLOC ; if not null, allocate new string (in ix = variable address)
913
ld bc, LIT_NULL_STR ; else, set to a null string literal
914
ld (ix + 4), c ; variable address low
915
ld (ix + 5), b ; variable address high
917
LET.ALLOC: push ix ; save variable address
918
ld l, (ix + 4) ; source string address low
919
ld h, (ix + 5) ; source string address high
920
push hl ; save copy from address
921
ld c, (ix + 3) ; get variable length
923
inc bc ; string length have one more byte from zero terminator
924
push bc ; save variable lenght + 1
925
call memory.alloc ; in bc = size, out ix = address, nz=OK
927
push ix ; just to transfer memory address from ix to de
929
pop bc ; restore bytes to be copied
930
pop hl ; restore copy from string address
931
push de ; save copy to address
932
ldir ; copy bc bytes from hl (data address) to de (variable address)
935
pop de ; restore copy to address
936
pop ix ; restore variable address
937
ld (ix + 4), e ; put memory address low into variable
938
ld (ix + 5), d ; put memory address high into variable
939
ret ; variable assigned
944
pop.parm ; get parameter boolean result in hl
947
ld a, (ix+5) ; put boolean integer result in a
953
if defined EXIST_DATA_SET
955
jp z, gfxClearTileScreen
958
__call_bios BIOS_CLS ; clear screen
964
call CLEAR.DAC ; put zero in DAC
965
pop.parm ; get parameter
966
call COPY_TO.ARG ; put in ARG
967
cp 2 ; test if integer
969
cp 4 ; test if single
971
cp 8 ; test if double
978
call MATH.PARM.POP ; get parameters into DAC/ARG
979
ld a, (BASIC_VALTYP) ;
980
cp 2 ; test if integer
981
jp z, MATH.MULT.INT ;
982
cp 3 ; test if string
984
cp 4 ; test if single
985
jp z, MATH.MULT.SGL ;
986
jp MATH.MULT.DBL ; it is a double
991
call MATH.PARM.POP ; get parameters into DAC/ARG
992
ld a, (BASIC_VALTYP) ;
993
cp 2 ; test if integer
995
cp 3 ; test if string
996
jp z, STRING.CONCAT ;
997
cp 4 ; test if single
999
jp MATH.ADD.DBL ; it is a double
1004
call MATH.PARM.POP ; get parameters into DAC/ARG
1005
ld a, (BASIC_VALTYP) ;
1006
cp 2 ; test if integer
1007
jp z, MATH.DIV.INT ;
1008
cp 3 ; test if string
1010
cp 4 ; test if single
1011
jp z, MATH.DIV.SGL ;
1012
jp MATH.DIV.DBL ; it is a double
1017
call MATH.PARM.POP ; get parameters into DAC/ARG
1018
ld a, (BASIC_VALTYP) ;
1019
cp 2 ; test if integer
1020
jp z, MATH.SUB.INT ;
1021
cp 3 ; test if string
1023
cp 4 ; test if single
1024
jp z, MATH.SUB.SGL ;
1025
jp MATH.SUB.DBL ; it is a double
1030
pop.parm ; get first parameter
1033
ret z ; return if string size zero
1034
if defined EXIST_DATA_SET
1035
ld (BIOS_TEMP), a ; size of string
1039
; discard if first char < 32 or > 126
1046
; adjust default color
1050
sra a ; Y / 8 = bank
1059
call gfxSetTileDefaultColor
1066
ld hl, (BIOS_GRPACY)
1068
;call MATH.MULT.16 ; slow y * 32
1078
ld de, (BIOS_GRPACX)
1080
ld de, (BIOS_GRPNAM)
1094
; abstract virtual GOTO
1097
;---------------------------------------------------------------------------------------------------------
1098
; MSX BASIC SUPPORT CODE
1099
;---------------------------------------------------------------------------------------------------------
1101
if defined ON_ERROR or defined ON_INTERVAL or defined ON_KEY_START or defined ON_SPRITE or defined ON_STOP or defined ON_STRIG_START or defined TRAP_ENABLED or defined TRAP_DISABLED or defined TRAP_PAUSE or defined TRAP_UNPAUSE
1105
RUN_TRAPS.1: push hl
1116
; in hl = trap block address (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
1118
ld a, (hl) ; trap status
1119
cp 5 ; trap occured AND trap not paused AND trap enabled ?
1120
ret nz ; return if false
1122
ld e, (hl) ; get trap address
1129
ret z ; return if address zero
1131
__call_basic BASIC_TRAP_ACKNW
1132
__call_basic BASIC_TRAP_PAUSE
1133
ld hl, TRAP_HANDLER.1
1134
ld a, (BASIC_ONGSBF) ; save traps execution
1137
ld (BASIC_ONGSBF), a ; disable traps execution
1138
push hl ; next return will be to trap handler
1139
push de ; indirect jump to trap address
1141
TRAP_HANDLER.1: pop af
1142
ld (BASIC_ONGSBF), a ; restore traps execution
1145
cp 1 ; trap enabled?
1147
__call_basic BASIC_TRAP_UNPAUSE
1150
; hl = trap block, de = trap handler
1152
ld (hl), a ; trap block status
1154
ld (hl), e ; trap block handler (pointer)
1161
if defined SET_PLAY_VOICE_1 or defined SET_PLAY_VOICE_2 or defined SET_PLAY_VOICE_3 or defined DO_PLAY or defined MUSIC_PLAY or defined MUSIC_NEXT or defined MUSIC_STOP
1164
ld (BIOS_TEMP), a ; save voice number
1168
ret nz ; return if not string
1171
ld (BIOS_TEMP2), a ; save string size
1172
push hl ; string address
1173
ld a, (BIOS_TEMP) ; restore voice number
1174
call BIOS_GETVCP ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
1176
ld a, (BIOS_TEMP2) ; restore string size
1177
ld (hl), a ; string size
1179
ld (hl), e ; string address
1183
ld D,H ; voice stack
1198
ld hl, BIOS_TEMP ; voice count
1212
__call_basic BASIC_PLAY_DIRECT
1219
;---------------------------------------------------------------------------------------------------------
1220
; VARIABLES ROUTINES
1221
;---------------------------------------------------------------------------------------------------------
1223
; input hl = variable address
1224
; input bc = variable name
1225
; input d = variable type
1226
INIT_VAR: ld (hl), d ; variable type
1228
ld (hl), c ; variable name 1
1230
ld (hl), b ; variable name 2
1244
CLEAR.VAR.LOOP: inc hl
1245
ld (hl), 0 ; data address/value
1248
; input HL = variable address
1249
; input A = variable output type
1250
; output HL = casted data address
1260
; input HL = variable address
1261
; output HL = variable address
1262
CAST_TO.INT: ;push af
1267
jp z, CAST_STR_TO.INT
1269
jp z, CAST_SGL_TO.INT
1271
jp z, CAST_DBL_TO.INT
1274
; input HL = variable address
1275
; output HL = variable address
1276
CAST_TO.STR: ;push af
1279
jp z, CAST_INT_TO.STR
1283
jp z, CAST_SGL_TO.STR
1285
jp z, CAST_DBL_TO.STR
1288
; input HL = variable address
1289
; output HL = variable address
1290
CAST_TO.SGL: ;push af
1293
jp z, CAST_INT_TO.SGL
1295
jp z, CAST_STR_TO.SGL
1299
jp z, CAST_DBL_TO.SGL
1302
; input HL = variable address
1303
; output HL = variable address
1304
CAST_TO.DBL: ;push af
1307
jp z, CAST_INT_TO.DBL
1309
jp z, CAST_STR_TO.DBL
1311
jp z, CAST_SGL_TO.DBL
1316
CAST_SGL_TO.STR: ; same as CAST_INT_TO.STR
1317
CAST_DBL_TO.STR: ; same as CAST_INT_TO.STR
1318
CAST_INT_TO.STR: call COPY_TO.DAC
1320
__call_bios MATH_FOUT ; convert DAC to string
1323
CAST_INT_TO.SGL: call COPY_TO.DAC
1324
__call_bios MATH_FRCSGL
1327
CAST_INT_TO.DBL: call COPY_TO.DAC
1328
__call_bios MATH_FRCDBL
1331
CAST_SGL_TO.INT: ; same as CAST_DBL_TO.INT
1332
CAST_DBL_TO.INT: call COPY_TO.DAC
1333
__call_bios MATH_FRCINT
1336
CAST_STR_TO.INT: call CAST_STR_TO.VAL ;
1337
__call_bios MATH_FRCINT ;
1340
CAST_STR_TO.SGL: call CAST_STR_TO.VAL ;
1341
__call_bios MATH_FRCSGL ;
1344
CAST_STR_TO.DBL: call CAST_STR_TO.VAL ;
1345
__call_bios MATH_FRCDBL ;
1348
CAST_STR_TO.VAL: call GET_STR.ADDR ;
1350
__call_bios MATH_FIN ; convert string to a value type
1353
GET_INT.VALUE: inc hl ; output BC with integer value
1359
CAST_SGL_TO.DBL: ; same as GET_DBL.ADDR
1360
CAST_DBL_TO.SGL: ; same as GET_DBL.ADDR
1361
GET_INT.ADDR: ; same as GET_DBL.ADDR
1362
GET_SGL.ADDR: ; same as GET_DBL.ADDR
1363
GET_DBL.ADDR: inc hl
1368
GET_STR.ADDR: push hl
1374
; input hl = string address
1375
; output b = string length
1376
GET_STR.LENGTH: ld b, 0
1377
GET_STR.LEN.NEXT: ld a, (hl)
1384
jr z, GET_STR.LEN.ERR
1386
GET_STR.LEN.ERR: ld b, 0
1388
STRING.COMPARE: ld ix, (BASIC_DAC+1) ; string 1
1389
ld iy, (BASIC_ARG+1) ; string 2
1390
STRING.COMPARE.NX: ld a, (ix) ; next char from string 1
1391
cp (iy) ; char s1 = char s2?
1392
jr nz, STRING.COMPARE.NE ; if not equal...
1394
jr z, STRING.COMPARE.F1 ; if string 1 has finished...
1395
ld a, (iy) ; next char from string 2
1397
jr z, STRING.COMPARE.GT ; if s2 has finished, s1 has not finished yet, so s1 is greater than s2
1400
jr STRING.COMPARE.NX ; get next char pair
1401
STRING.COMPARE.F1: ld a, (iy) ; verify if string 2 has finished too
1403
jr z, STRING.COMPARE.EQ ; if s2 has finished, then they are equals
1404
jr STRING.COMPARE.LT ; else, result = s1 is less than s2
1405
STRING.COMPARE.NE: jr c, STRING.COMPARE.GT ; verify if s1 is greater than s2...
1406
STRING.COMPARE.LT: ld a, 1 ; ...else, result = s1 less than s2
1408
STRING.COMPARE.GT: ld a, 0xFF ; result = s1 is greater than s2
1410
STRING.COMPARE.EQ: xor a ; result = s1 is equal to s2
1412
STRING.CONCAT: ld ix, BASIC_DAC ; s1 size
1413
ld a, (BASIC_ARG) ; s2 size
1414
add a, (ix) ; s3 size = s1 size + s2 size
1418
inc bc ; add 1 byte to size
1419
call memory.alloc ; in bc size, out ix new memory address, nz=OK
1420
jp z, memory.error ;
1424
ld a, (BASIC_DAC) ; s1 size
1425
ld hl, (BASIC_DAC + 1) ; string 1
1426
call COPY_TO.STR ; copy to new memory
1427
ld a, (BASIC_ARG) ; s2 size
1428
ld hl, (BASIC_ARG + 1) ; string 2
1429
call COPY_TO.STR ; copy to new memory
1431
ld (de), a ; null terminated
1434
call COPY_TO.VAR_DUMMY.STR ;
1435
ret.parm ; WARNING - VERIFY STRING MEMORY LEAKs
1436
STRING.PRINT: ld a, (BIOS_SCRMOD) ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode
1438
jr nc, STRING.PRINT.G2 ; jump if graphic screen mode MSX2 (>=5)
1440
jr nc, STRING.PRINT.G1 ; jump if graphic screen mode MSX1 (>=2)
1441
STRING.PRINT.T: ld a, (hl) ; get a char from a string parameter
1442
or a ; cp 0 - is it the string end?
1444
__call_bios BIOS_CHPUT ; put the char (a) into text screen
1446
jr STRING.PRINT.T ; repeat
1447
STRING.PRINT.G1: ld a, (hl) ; get a char from a string parameter
1448
or a ; cp 0 - is it the string end?
1450
__call_bios BIOS_GRPPRT ; put the char (a) into graphical screen
1452
jr STRING.PRINT.G1 ; repeat
1453
STRING.PRINT.G2: ld a, (hl) ; get a char from a string parameter
1454
or a ; cp 0 - is it the string end?
1456
ld ix, BIOS_GRPPRT2 ; put the char (a) into graphical screen
1459
jr STRING.PRINT.G2 ; repeat
1461
; a = string size to copy
1462
; input hl = string from
1463
; input de = string to
1465
ret z ; avoid copy if size = zero
1467
ld c, a ; string size
1468
ldir ; copy bc bytes from hl to de
1470
COPY_TO.BASIC_BUF: ld bc, BASIC_BUF
1471
ld a, (LIT_QUOTE_CHAR)
1474
COPY_BAS_BUF.LOOP: ld a, (hl)
1476
jr z, COPY_BAS_BUF.EXIT
1480
jr COPY_BAS_BUF.LOOP
1481
COPY_BAS_BUF.EXIT: ld a, (LIT_QUOTE_CHAR)
1488
COPY_TO.VAR_DUMMY: ld a, (BASIC_VALTYP) ; create dummy variable from VALTYPE
1490
jr nz, COPY_TO.VAR_DUMMY.DBL
1492
call GET_STR.LENGTH ; get string length
1494
ld a, b ; string length
1495
COPY_TO.VAR_DUMMY.STR: call GET_VAR_DUMMY.ADDR ; create dummy string variable from HL
1496
ld (ix), 3 ; data type string
1498
ld (ix+2), 255 ; var type fixed
1499
ld (ix+3), a ; string length
1500
ld (ix+4), l ; data address low
1501
ld (ix+5), h ; data address high
1502
;call GET_STR.LENGTH ; get string length
1503
;ld (ix+3), b ; string length
1504
push ix ; output var address...
1507
COPY_TO.VAR_DUMMY.INT: call GET_VAR_DUMMY.ADDR ; create dummy integer variable from BC
1508
ld (ix), 2 ; data type string
1519
push ix ; output var address...
1522
COPY_TO.VAR_DUMMY.DBL: call GET_VAR_DUMMY.ADDR ; create dummy value variable from DAC
1523
ld (ix), a ; data type
1528
push ix ; just to copy ix to de
1533
ldir ; copy bc bytes from hl (data address) to de (variable address)
1534
push ix ; output var address...
1537
GET_VAR_DUMMY.ADDR: push af ;
1540
ld ix, (VAR_DUMMY.POINTER) ;
1541
ld a, (VAR_DUMMY.COUNTER) ;
1542
GET_VAR_DUMMY.NEXT: add ix, de ;
1545
jr nz, GET_VAR_DUMMY.EXIT ;
1547
ld ix, VAR_DUMMY.DATA ;
1548
GET_VAR_DUMMY.EXIT: ld (VAR_DUMMY.POINTER), ix ;
1549
ld (VAR_DUMMY.COUNTER), a ;
1550
ld a, (ix) ; get last var dummy type
1551
cp 3 ; is it string?
1552
call z, GET_VAR_DUMMY.FREE ; free string memory
1559
ld l, (ix+4) ; get string data address
1563
call memory.free ; free memory
1567
; input hl = variable address
1568
COPY_TO.DAC: ld de, BASIC_DAC
1569
COPY_TO.DAC.DATA: ld a, (hl)
1570
ld (BASIC_VALTYP), a
1574
ld bc, 8 ; data = 8 bytes
1575
ldir ; copy bc bytes from hl (data address) to de (variable address)
1577
COPY_TO.ARG: ld de, BASIC_ARG ;
1578
jr COPY_TO.DAC.DATA ;
1579
COPY_TO.DAC_ARG: ld hl, BASIC_DAC ;
1581
ld bc, 8 ; data = 8 bytes
1582
ldir ; copy bc bytes from hl (data address) to de (variable address)
1584
COPY_TO.ARG_DAC: ld hl, BASIC_ARG ;
1586
ld bc, 8 ; data = 8 bytes
1587
ldir ; copy bc bytes from hl (data address) to de (variable address)
1589
COPY_TO.DAC_TMP: ld hl, BASIC_DAC ;
1590
ld de, BASIC_SWPTMP ;
1591
ld bc, 8 ; data = 8 bytes
1592
ldir ; copy bc bytes from hl (data address) to de (variable address)
1594
COPY_TO.TMP_DAC: ld hl, BASIC_SWPTMP ;
1596
ld bc, 8 ; data = 8 bytes
1597
ldir ; copy bc bytes from hl (data address) to de (variable address)
1600
exx ; save registers
1603
ld de, BASIC_SWPTMP ;
1604
ldir ; copy bc bytes from hl to de
1608
ldir ; copy bc bytes from hl to de
1610
ld hl, BASIC_SWPTMP ;
1612
ldir ; copy bc bytes from hl to de
1613
exx ; restore registers
1616
CLEAR.DAC: ld de, BASIC_DAC
1617
CLEAR.DAC.DATA: ld hl, BASIC_VALTYP
1620
ld bc, 8 ; data = 8 bytes
1621
ldir ; copy bc bytes from hl (data address) to de (variable address)
1623
CLEAR.ARG: ld de, BASIC_ARG
1628
;---------------------------------------------------------------------------------------------------------
1629
; MATH 16 BITS ROUTINES
1630
;---------------------------------------------------------------------------------------------------------
1632
MATH.PARM.POP: pop af ; get PC from caller stack
1633
ex af, af' ; save PC to temp
1634
pop.parm ; get first parameter
1635
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1636
pop.parm ; get second parameter
1637
ex af, af' ; restore PC from temp
1638
push af ; put again PC from caller in stack
1639
ex af, af' ; restore 1st data type
1640
push af ; save 1st data type
1641
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1642
pop bc ; restore 1st data type (ARG) in B
1643
cp b ; test if data type in A (DAC) = data type in B (ARG)
1644
ret z ; return if is equal data types
1645
MATH.PARM.CAST: push bc ; else cast both to double
1646
and 12 ; test if single/double
1647
jr nz, MATH.PARM.CST1 ; avoid cast if already single/double
1648
__call_bios MATH_FRCDBL ; convert DAC to double
1649
MATH.PARM.CST1: pop af ;
1650
and 12 ; test if single/double
1651
jr nz, MATH.PARM.CST2 ; avoid cast if already single/double
1652
ld (BASIC_VALTYP), a ;
1653
call COPY_TO.DAC_TMP ;
1654
call COPY_TO.ARG_DAC ;
1655
__call_bios MATH_FRCDBL ; convert ARG to double
1656
call COPY_TO.DAC_ARG ;
1657
call COPY_TO.TMP_DAC ;
1658
MATH.PARM.CST2: ld a, 8 ;
1659
ld (BASIC_VALTYP), a ;
1661
MATH.PARM.POP.INT: ; return result in DAC/ARG as integer
1662
pop af ; get PC from caller stack
1663
ex af, af' ; save PC to temp
1664
pop.parm ; get first parameter
1665
ld a, (hl) ; get parameter type
1666
and 2 ; test if integer
1667
jr z, MATH.PARM.POP.I1 ; do cast if not integer
1668
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1669
jr MATH.PARM.POP.I2 ; go to next parameter
1670
MATH.PARM.POP.I1: call COPY_TO.DAC ; put HL in DAC (return var type in A)
1671
__call_bios MATH_FRCINT ; convert DAC to int
1672
call COPY_TO.DAC_ARG ; copy DAC to ARG
1673
MATH.PARM.POP.I2: pop.parm ; get second parameter
1674
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1675
and 2 ; test if integer
1676
jr nz, MATH.PARM.POP.I3 ; avoid cast if already integer
1677
__call_bios MATH_FRCINT ; convert DAC to int
1679
ld (BASIC_VALTYP), a ;
1681
ex af, af' ; restore PC from temp
1682
push af ; put again PC from caller in stack
1684
MATH.PARM.PUSH: call COPY_TO.VAR_DUMMY ;
1690
; output in parm stack
1691
; http://www.z80.info/zip/zaks_book.pdf - page 104
1692
MATH.ADD.INT: ld hl, (BASIC_DAC+2) ;
1693
ld bc, (BASIC_ARG+2) ;
1695
ld (BASIC_DAC+2), hl ;
1700
if defined MATH.SUB or defined MATH.NEG
1703
; output in parm stack
1704
; http://www.z80.info/zip/zaks_book.pdf - page 104
1705
MATH.SUB.INT: ld hl, (BASIC_DAC+2) ;
1706
ld de, (BASIC_ARG+2) ;
1709
ld (BASIC_DAC+2), hl ;
1714
if defined MATH.MULT
1717
; output in parm stack
1718
MATH.MULT.INT: ld hl, (BASIC_DAC+2) ;
1719
ld bc, (BASIC_ARG+2) ;
1721
ld (BASIC_DAC+2), hl ;
1724
; input HL = multiplicand
1725
; input BC = multiplier
1726
; output HL = result
1727
; http://www.z80.info/zip/zaks_book.pdf - page 131
1728
MATH.MULT.16: ld a, c ; low multiplier
1729
ld c, b ; high multiplier
1731
ld d, h ; multiplicand
1734
MULT16LOOP: srl c ; right shift multiplier high
1735
rra ; rotate right multiplier low
1736
jr nc, MULT16NOADD ; test carry
1737
add hl, de ; add multiplicand to result
1738
MULT16NOADD: ex de, hl
1739
add hl, hl ; double - shift multiplicand
1746
if defined MATH.DIV or defined MATH.IDIV or defined MATH.MOD
1748
; input AC = dividend
1749
; input DE = divisor
1750
; output AC = quotient
1751
; output HL = remainder
1752
; http://www.z80.info/zip/zaks_book.pdf - page 140
1753
MATH.DIV.16: ld hl, 0 ; clear accumulator
1754
ld b, 16 ; set counter
1755
DIV16LOOP: rl c ; rotate accumulator result left
1757
adc hl, hl ; left shift
1758
sbc hl, de ; trial subtract divisor
1759
jr nc, $ + 3 ; subtract was OK ($ = current location)
1760
add hl, de ; restore accumulator
1761
ccf ; calculate result bit
1762
djnz DIV16LOOP ; counter not zero
1763
rl c ; shift in last result bit
1769
if defined GFX_FAST or defined LINE
1771
; compare two signed 16 bits integers
1772
; HL < DE: Carry flag
1773
; HL = DE: Zero flag
1774
; http://www.z80.info/zip/zaks_book.pdf - page 531
1775
MATH.COMP.S16: ld a, h ; test high order byte
1776
and 0x80 ; test sign, clear carry
1777
jr nz, MATH.COMP.S16.NEGM1 ; jump if hl is negative
1779
ret nz ; de is negative (and hl is positive)
1781
cp d ; signs are both positive, so normal compare
1783
ld a, l ; test low order byte
1786
MATH.COMP.S16.NEGM1:
1788
rla ; sign bit into carry
1789
ret c ; signs different
1791
cp d ; both signs negative
1801
MATH.ADD.SGL: ld a, 8 ;
1802
ld (BASIC_VALTYP), a ;
1803
MATH.ADD.DBL: __call_bios MATH_DECADD ;
1808
if defined MATH.SUB or defined MATH.NEG
1810
MATH.SUB.SGL: ld a, 8 ;
1811
ld (BASIC_VALTYP), a ;
1812
MATH.SUB.DBL: __call_bios MATH_DECSUB ;
1817
if defined MATH.MULT
1819
MATH.MULT.SGL: ld a, 8 ;
1820
ld (BASIC_VALTYP), a ;
1821
MATH.MULT.DBL: __call_bios MATH_DECMUL ;
1829
; output in parm stack
1830
MATH.DIV.INT: __call_bios MATH_FRCDBL ; convert DAC to double
1833
ld (BASIC_VALTYP), a ;
1834
__call_bios MATH_FRCDBL ; convert ARG to double
1836
MATH.DIV.SGL: ld a, 8 ;
1837
ld (BASIC_VALTYP), a ;
1838
MATH.DIV.DBL: __call_bios MATH_DECDIV ;
1843
if defined MATH.IDIV
1846
; output in parm stack
1847
MATH.IDIV.SGL: ld a, 8 ;
1848
ld (BASIC_VALTYP), a ;
1849
MATH.IDIV.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1852
ld (BASIC_VALTYP), a ;
1853
__call_bios MATH_FRCINT ; convert ARG to integer
1855
MATH.IDIV.INT: ld hl, (BASIC_DAC+2) ;
1858
ld de, (BASIC_ARG+2) ;
1862
ld (BASIC_DAC+2), hl ; quotient
1869
MATH.POW.INT: ld (BASIC_VALTYP), a ;
1870
__call_bios MATH_FRCDBL ; convert DAC to double
1873
ld (BASIC_VALTYP), a ;
1874
__call_bios MATH_FRCDBL ; convert ARG to double
1876
MATH.POW.SGL: ld a, 8 ;
1877
ld (BASIC_VALTYP), a ;
1878
MATH.POW.DBL: __call_bios MATH_DBLEXP ;
1885
;MATH.MOD.SGL: ld a, 8 ;
1886
; ld (BASIC_VALTYP), a ;
1887
;MATH.MOD.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1888
; call SWAP.DAC.ARG ;
1890
; ld (BASIC_VALTYP), a ;
1891
; __call_bios MATH_FRCINT ; convert ARG to integer
1892
; call SWAP.DAC.ARG ;
1893
MATH.MOD.INT: ld hl, (BASIC_DAC+2) ;
1896
ld de, (BASIC_ARG+2) ;
1898
ld (BASIC_DAC+2), hl ; remainder
1905
; fast 16-bit integer square root
1906
; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html
1907
; 92 bytes, 344-379 cycles (average 362)
1908
; v2 - 3 t-state optimization spotted by Russ McNulty
1909
; call with hl = number to square root
1910
; returns a = square root
1987
if defined RANDOMIZE or defined SEED
1989
MATH.RANDOMIZE: di ;
1990
ld bc, (BIOS_JIFFY) ;
1993
MATH.SEED: ld (BASIC_RNDX), bc ; seed to IRND
1994
push bc ; in bc = new integer seed
1998
ld (BASIC_DAC+2), bc ; copy bc to dac
1999
ld a, 2 ; type integer
2000
ld (BASIC_VALTYP), a ;
2001
__call_bios MATH_FRCDBL ; convert DAC integer to DAC double
2002
__call_bios MATH_NEG ; DAC = -DAC
2003
__call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
2008
MATH.ERROR: ld e, 13 ; type mismatch
2009
__call_basic BASIC_ERROR_HANDLER ;
2013
;---------------------------------------------------------------------------------------------------------
2015
;---------------------------------------------------------------------------------------------------------
2017
BOOLEAN.RET.TRUE: ld hl, LIT_TRUE ;
2019
BOOLEAN.RET.FALSE: ld hl, LIT_FALSE ;
2021
BOOLEAN.CMP.INT: ld hl, (BASIC_DAC+2) ;
2022
ld de, (BASIC_ARG+2) ;
2023
__call_bios MATH_ICOMP ;
2025
BOOLEAN.CMP.SGL: ld bc, (BASIC_ARG) ;
2026
ld de, (BASIC_ARG+2) ;
2027
__call_bios MATH_DCOMP ;
2029
BOOLEAN.CMP.DBL: __call_bios MATH_XDCOMP ;
2031
BOOLEAN.CMP.STR: call STRING.COMPARE ;
2034
if defined BOOLEAN.GT
2036
BOOLEAN.GT.INT: call BOOLEAN.CMP.INT ;
2038
BOOLEAN.GT.STR: call BOOLEAN.CMP.STR ;
2040
BOOLEAN.GT.SGL: call BOOLEAN.CMP.SGL ;
2042
BOOLEAN.GT.DBL: call BOOLEAN.CMP.DBL ;
2044
BOOLEAN.GT.RET: cp 0x01 ;
2045
jp z, BOOLEAN.RET.TRUE ;
2046
jp BOOLEAN.RET.FALSE ;
2049
if defined BOOLEAN.LT
2051
BOOLEAN.LT.INT: call BOOLEAN.CMP.INT ;
2053
BOOLEAN.LT.STR: call BOOLEAN.CMP.STR ;
2055
BOOLEAN.LT.SGL: call BOOLEAN.CMP.SGL ;
2057
BOOLEAN.LT.DBL: call BOOLEAN.CMP.DBL ;
2059
BOOLEAN.LT.RET: cp 0xFF ;
2060
jp z, BOOLEAN.RET.TRUE ;
2061
jp BOOLEAN.RET.FALSE ;
2065
if defined BOOLEAN.GE
2067
BOOLEAN.GE.INT: call BOOLEAN.CMP.INT ;
2069
BOOLEAN.GE.STR: call BOOLEAN.CMP.STR ;
2071
BOOLEAN.GE.SGL: call BOOLEAN.CMP.SGL ;
2073
BOOLEAN.GE.DBL: call BOOLEAN.CMP.DBL ;
2075
BOOLEAN.GE.RET: cp 0x01 ;
2076
jp z, BOOLEAN.RET.TRUE ;
2078
jp z, BOOLEAN.RET.TRUE ;
2079
jp BOOLEAN.RET.FALSE ;
2083
if defined BOOLEAN.LE
2085
BOOLEAN.LE.INT: call BOOLEAN.CMP.INT ;
2087
BOOLEAN.LE.STR: call BOOLEAN.CMP.STR ;
2089
BOOLEAN.LE.SGL: call BOOLEAN.CMP.SGL ;
2091
BOOLEAN.LE.DBL: call BOOLEAN.CMP.DBL ;
2093
BOOLEAN.LE.RET: cp 0xFF ;
2094
jp z, BOOLEAN.RET.TRUE ;
2096
jp z, BOOLEAN.RET.TRUE ;
2097
jp BOOLEAN.RET.FALSE ;
2101
if defined BOOLEAN.NE
2103
BOOLEAN.NE.INT: call BOOLEAN.CMP.INT ;
2105
BOOLEAN.NE.STR: call BOOLEAN.CMP.STR ;
2107
BOOLEAN.NE.SGL: call BOOLEAN.CMP.SGL ;
2109
BOOLEAN.NE.DBL: call BOOLEAN.CMP.DBL ;
2111
BOOLEAN.NE.RET: or a ; cp 0
2112
jp nz, BOOLEAN.RET.TRUE ;
2113
jp BOOLEAN.RET.FALSE ;
2117
if defined BOOLEAN.EQ
2119
BOOLEAN.EQ.INT: call BOOLEAN.CMP.INT ;
2121
BOOLEAN.EQ.STR: call BOOLEAN.CMP.STR ;
2123
BOOLEAN.EQ.SGL: call BOOLEAN.CMP.SGL ;
2125
BOOLEAN.EQ.DBL: call BOOLEAN.CMP.DBL ;
2127
BOOLEAN.EQ.RET: or a ; cp 0
2128
jp z, BOOLEAN.RET.TRUE ;
2129
jp BOOLEAN.RET.FALSE ;
2133
if defined BOOLEAN.AND
2135
BOOLEAN.AND.INT: ld a, (BASIC_DAC+2) ;
2136
ld hl, BASIC_ARG+2 ;
2138
ld (BASIC_DAC+2), a ;
2140
ld a, (BASIC_DAC+3) ;
2142
ld (BASIC_DAC+3), a ;
2148
if defined BOOLEAN.OR
2150
BOOLEAN.OR.INT: ld a, (BASIC_DAC+2) ;
2151
ld hl, BASIC_ARG+2 ;
2153
ld (BASIC_DAC+2), a ;
2155
ld a, (BASIC_DAC+3) ;
2157
ld (BASIC_DAC+3), a ;
2163
if defined BOOLEAN.XOR
2165
BOOLEAN.XOR.INT: ld a, (BASIC_DAC+2) ;
2166
ld hl, BASIC_ARG+2 ;
2168
ld (BASIC_DAC+2), a ;
2170
ld a, (BASIC_DAC+3) ;
2172
ld (BASIC_DAC+3), a ;
2178
if defined BOOLEAN.EQV
2180
BOOLEAN.EQV.INT: ld a, (BASIC_DAC+2) ;
2181
ld hl, BASIC_ARG+2 ;
2184
ld (BASIC_DAC+2), a ;
2186
ld a, (BASIC_DAC+3) ;
2189
ld (BASIC_DAC+3), a ;
2195
if defined BOOLEAN.IMP
2197
BOOLEAN.IMP.INT: ld a, (BASIC_DAC+2) ;
2198
ld hl, BASIC_ARG+2 ;
2201
ld (BASIC_DAC+2), a ;
2203
ld a, (BASIC_DAC+3) ;
2206
ld (BASIC_DAC+3), a ;
2212
if defined BOOLEAN.SHR
2214
BOOLEAN.SHR.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to right (bits 15...0-->)
2215
ld a, (BASIC_ARG+2) ;
2217
jp z, MATH.PARM.PUSH ; return if not shift
2218
ld b, a ; shift count
2219
BOOLEAN.SHR.INT.N: rr (ix+1) ;
2222
djnz BOOLEAN.SHR.INT.N ; next shift
2224
jp MATH.PARM.PUSH ; return DAC
2228
if defined BOOLEAN.SHL
2230
BOOLEAN.SHL.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to left (<--bits 15...0)
2231
ld a, (BASIC_ARG+2) ;
2233
jp z, MATH.PARM.PUSH ; return if not shift
2234
ld b, a ; shift count
2235
BOOLEAN.SHL.INT.N: rl (ix) ;
2238
djnz BOOLEAN.SHL.INT.N ; next shift
2240
jp MATH.PARM.PUSH ; return DAC
2244
if defined BOOLEAN.NOT
2246
BOOLEAN.NOT.INT: ld a, (BASIC_DAC+2) ;
2248
ld (BASIC_DAC+2), a ;
2249
ld a, (BASIC_DAC+3) ;
2251
ld (BASIC_DAC+3), a ;
2259
;---------------------------------------------------------------------------------------------------------
2260
; MEMORY ALLOCATION ROUTINES
2261
;---------------------------------------------------------------------------------------------------------
2262
; Adapted from memory allocator code by SamSaga2, Spain, 2015
2263
; https://www.msx.org/forum/msx-talk/development/asm-memory-allocator
2264
; https://www.msx.org/users/samsaga2
2265
;---------------------------------------------------------------------------------------------------------
2266
memory.heap_start: equ VAR_STACK.END + 1 ; start at end of variable stack
2267
memory.heap_end: equ 0xF0A0 - 100 ; end at start of work area for stack (100 bytes reserved), BIOS and BASIC interpreter
2268
block.next: equ 0 ; next free block address
2269
block.size: equ 2 ; size of block including header
2270
block: equ 4 ; block.next + block.size
2274
ld ix,memory.heap_start ; first block
2275
ld hl,memory.heap_start+block ; second block
2276
;; first block NEXT=secondblock, SIZE=0
2277
;; with this block we have a fixed start location
2278
;; because never will be allocated
2279
ld (ix+block.next),l
2280
ld (ix+block.next+1),h
2281
ld (ix+block.size),0
2282
ld (ix+block.size+1),0
2283
;; second block NEXT=0, SIZE=all
2284
;; the first and only free block have all available memory
2285
ld (ix+block.next+block),0
2286
ld (ix+block.next+block+1),0
2288
;ld hl,memory.heap_end ; size = @heap_end (stack) - heap_start - block_header * 2 - 100 (buffer for stack)
2291
ld de, memory.heap_start + (block * 2) + 100
2293
;ld de, block * 2 + 100
2295
ld (ix+block.size+block),l
2296
ld (ix+block.size+block+1),h
2300
;; IN BC=size, OUT IX=memptr, NZ=ok
2308
ld ix,memory.heap_start ; this
2311
ld l,(ix+block.size)
2312
ld h,(ix+block.size+1)
2315
jp z, memory.alloc.exactfit
2316
jp c, memory.alloc.nextblock
2317
;; split found block
2318
memory.alloc.splitfit:
2319
;; free space must allow at least two blocks headers (current + next)
2321
jr nz, memory.alloc.splitfit.do ; if free space > 0xFF, do split
2324
jr c, memory.alloc.nextblock ; if free space < 4, skip to next block
2325
memory.alloc.splitfit.do:
2326
;; newfreeblock = this + BC
2330
;; prevblock->next = newfreeblock
2331
ld (iy+block.next),l
2332
ld (iy+block.next+1),h
2333
;; newfreeblock->next = this->next
2335
pop iy ; iy = newfreeblock
2336
ld l,(ix+block.next)
2337
ld h,(ix+block.next+1)
2338
ld (iy+block.next),l
2339
ld (iy+block.next+1),h
2340
;; newfreeblock->size = this->size - BC
2341
ld l,(ix+block.size)
2342
ld h,(ix+block.size+1)
2345
ld (iy+block.size),l
2346
ld (iy+block.size+1),h
2348
ld (ix+block.size),c
2349
ld (ix+block.size+1),b
2351
;; use whole found block
2352
memory.alloc.exactfit:
2353
;; prevblock->next = this->next - remove block from free list
2354
ld l,(ix+block.next)
2355
ld h,(ix+block.next+1)
2356
ld (iy+block.next),l
2357
ld (iy+block.next+1),h
2366
memory.alloc.nextblock:
2367
ld l,(ix+block.next)
2368
ld h,(ix+block.next+1)
2375
;; this = this->next
2378
jp memory.alloc.find
2383
;; HL = IX - block_header_size
2390
ld ix,memory.heap_start
2392
ld e,(ix+block.next)
2393
ld d,(ix+block.next+1)
2396
jp z, memory.free.passedend
2397
sbc hl,de ; test this (HL) against next (DE)
2398
jr c, memory.free.found ; if DE > HL
2399
add hl,de ; restore hl value
2401
pop ix ; current = next
2404
;; ix=prev, hl=this, de=next
2406
add hl,de ; restore hl value
2407
ld (ix+block.next), l
2408
ld (ix+block.next+1), h ; prev->next = this
2411
ld (iy+block.next), e
2412
ld (iy+block.next+1), d ; this->next = next
2413
push ix ; prev x this
2418
call memory.free.coalesce
2419
pop ix ; this x next
2420
jr memory.free.coalesce
2424
memory.free.coalesce:
2425
ld c, (iy+block.size)
2426
ld b, (iy+block.size+1) ; bc = this->size
2430
adc hl, bc ; hl = this + this->size
2434
sbc hl, de ; if this + this->size == next, then this->size += next->size, this->next = next->next
2435
jr z, memory.free.coalesce.do
2436
push ix ; else, new *this = *next
2439
memory.free.coalesce.do:
2440
ld l, (ix+block.size)
2441
ld h, (ix+block.size+1) ; hl = next->size
2443
adc hl, bc ; hl += this->size
2444
ld (iy+block.size), l
2445
ld (iy+block.size+1), h ; this->size = hl
2446
ld l, (ix+block.next)
2447
ld h, (ix+block.next+1) ; hl = next->next
2448
ld (iy+block.next), l
2449
ld (iy+block.next+1), h ; this->next = hl
2452
memory.free.passedend:
2453
;; append block at the end of the free list
2454
ld (ix+block.next),l
2455
ld (ix+block.next+1),h
2458
ld (iy+block.next),0
2459
ld (iy+block.next+1),0
2465
ld ix,memory.heap_start
2467
memory.get_free.count:
2469
add a,(ix+block.size)
2472
adc a,(ix+block.size+1)
2474
ld l,(ix+block.next)
2475
ld h,(ix+block.next+1)
2481
jr memory.get_free.count
2483
memory.error: ld e, 7 ; out of memory
2484
__call_basic BASIC_ERROR_HANDLER ;
2489
;---------------------------------------------------------------------------------------------------------
2491
;---------------------------------------------------------------------------------------------------------
2500
RET_MATH_LIB: call COPY_TO.TMP_DAC
2506
MATH_DECADD: ld ix, addSingle
2511
if defined MATH.SUB or defined MATH.NEG
2513
MATH_DECSUB: ld ix, subSingle
2518
if defined MATH.MULT
2520
MATH_DECMUL: ld ix, mulSingle
2527
MATH_DECDIV: ld ix, divSingle
2535
MATH_SNGEXP: ld ix, powSingle
2542
MATH_COS: ld ix, cosSingle
2549
MATH_SIN: ld ix, sinSingle
2556
MATH_TAN: ld ix, tanSingle
2563
MATH_ATN: ld ix, atanSingle
2570
MATH_SQR: ld ix, sqrtSingle
2577
MATH_LOG: ld ix, lnSingle
2584
MATH_EXP: ld ix, expSingle
2591
MATH_ABSFN: ld ix, absSingle
2596
if defined MATH.SEED or defined MATH.NEG
2598
MATH_NEG: ld ix, negSingle
2605
MATH_SGN: ld ix, sgnSingle
2610
if defined RND or defined MATH.SEED
2612
MATH_RND: ld ix, randSingle
2617
MATH_FRCINT: ld hl, BASIC_DAC
2630
ld (BASIC_VALTYP), a
2633
MATH_FRCDBL: ; same as MATH_FRCSGL
2634
MATH_FRCSGL: ld hl, BASIC_DAC+2 ; input address
2635
ld bc, BASIC_DAC ; output address
2638
ld (BASIC_VALTYP), a
2641
MATH_ICOMP: ld a, h ; cp hl, de (alternative to bios DCOMPR)
2643
jr nz, MATH_ICOMP.NE.HIGH
2646
jr nz, MATH_ICOMP.NE.LOW
2648
MATH_ICOMP.NE.HIGH: jr c, MATH_ICOMP.GT.HIGH
2650
jr nz, MATH_DCOMP.GT
2652
MATH_ICOMP.GT.HIGH: bit 7, d
2655
MATH_ICOMP.NE.LOW: jr c, MATH_DCOMP.GT
2658
MATH_XDCOMP: ; same as MATH_DCOMP
2659
MATH_DCOMP: ld ix, cmpSingle
2663
MATH_DCOMP.GT: ld a, 0xFF ; DAC > ARG
2665
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
2667
MATH_DCOMP.LT: ld a, 1 ; DAC < ARG
2670
if defined CAST_STR_TO.VAL
2672
MATH_FIN: ; HL has the source string
2673
ld a, (BASIC_VALTYP)
2674
cp 2 ; test if integer
2676
ld hl, (BASIC_DAC+2)
2681
MATH_FIN.1: ld BC, BASIC_DAC
2687
if defined CAST_INT_TO.STR
2689
MATH_FOUT: ld a, (BASIC_VALTYP)
2690
cp 2 ; test if integer
2692
ld hl, (BASIC_DAC+2)
2697
MATH_FOUT.1: ld hl, BASIC_DAC
2708
;---------------------------------------------------------------------------------------------------------
2710
; Copyright 2018 Zeda A.K. Thomas
2711
;---------------------------------------------------------------------------------------------------------
2713
; https://github.com/Zeda/z80float
2714
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
2715
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
2716
;---------------------------------------------------------------------------------------------------------
2718
; HL points to the first operand
2719
; DE points to the second operand (if needed)
2720
; IX points to the third operand (if needed, rare)
2721
; BC points to where the result should be output
2722
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
2723
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
2724
; exponent biased by +128.
2725
;---------------------------------------------------------------------------------------------------------
2726
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
2727
;---------------------------------------------------------------------------------------------------------
2729
;---------------------------------------------------------------------------------------------------------
2731
;---------------------------------------------------------------------------------------------------------
2733
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
2734
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
2735
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
2736
scrap: equ BASIC_HOLD8
2737
seed0: equ BASIC_RNDX
2738
seed1: equ seed0 + 4
2739
var48: equ scrap + 4
2742
addend2: equ scrap+7 ;4 bytes
2743
var_x: equ BASIC_HOLD8 + 4 ;4 bytes
2744
var_y: equ var_x + 4 ;4 bytes
2745
var_z: equ var_y + 4 ;4 bytes
2746
var_a: equ var_z + 4 ;4 bytes
2747
var_b: equ var_a + 4 ;4 bytes
2748
var_c: equ var_b + 4 ;4 bytes
2749
temp: equ var_c + 4 ;4 bytes
2750
temp1: equ temp + 4 ;4 bytes
2751
temp2: equ temp1 + 4 ;4 bytes
2752
temp3: equ temp2 + 4 ;4 bytes
2754
pow10exp_single: equ scrap+9
2755
strout_single: equ 0xF750 ; PARM2 - BASIC_BUF ;pow10exp_single+2
2757
;---------------------------------------------------------------------------------------------------------
2759
;---------------------------------------------------------------------------------------------------------
2761
;;Still need to tend to special cases
2829
pop hl ;bigger float
2961
;;Need to adjust sign flag
2984
;;How many push/pops are needed?
2992
;;How many push/pops are needed?
2998
;;How many push/pops are needed?
2999
;;Return bigger number
3006
;---------------------------------------------------------------------------------------------------------
3008
;---------------------------------------------------------------------------------------------------------
3031
jp addInject ;jumps in to the addSingle routine
3033
;---------------------------------------------------------------------------------------------------------
3035
;---------------------------------------------------------------------------------------------------------
3038
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
3039
;Outputs: float1*float2 is stored to (BC)
3040
;573+mul24+{0,35}+{0,30}
3043
;avg: 2055.13839751681cc
3069
;;return float in CHLB
3079
jr z,mulSingle_case0
3091
;jr z,mulSingle_case1
3095
jp z,mulSingle_case1
3100
rra ; |Lots of help from Runer112 and
3101
adc a,a ; |calc84maniac for optimizing
3102
jp po,bad ; |this exponent check.
3111
call mul24 ;BDE*CHL->HLBCDE, returns sign info
3168
;special*x = special
3189
;basically, if b|c has bit 5 set, return NaN
3222
;;avg :1464.9033203125cc (1464+925/1024)
3225
;avg: 1449.63839751681cc
3266
;---------------------------------------------------------------------------------------------------------
3268
;---------------------------------------------------------------------------------------------------------
3271
;;HL points to numerator
3272
;;DE points to denominator
3273
;;BC points to where the quotient gets written
3275
divSingle_no_pushpop:
3281
xor (hl) ; |Get sign of output
3288
ex de,hl ; |Get exponent
3395
call divsub1 ;34 or 66
3413
;34cc or 66cc or 93cc
3428
;---------------------------------------------------------------------------------------------------------
3430
; https://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
3431
; https://stackoverflow.com/questions/3518973/floating-point-exponentiation-without-power-function
3432
;---------------------------------------------------------------------------------------------------------
3433
;double mypow( double base, double power, double precision )
3435
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3436
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3437
; else if ( precision >= 1 ) {
3438
; if( base >= 0 ) return sqrt( base );
3439
; else return sqrt( -base );
3440
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3443
if defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN
3449
;;BC points to output
3453
ld bc, var_y ; power
3458
ld hl, const_precision
3459
ld bc, var_a ; precision
3462
ld bc, var_z ; result
3471
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3477
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3483
; else if ( precision >= 1 ) {
3484
; if( base >= 0 ) return sqrt( base );
3485
; else return sqrt( -base );
3491
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3516
; return 1 / mypow( base, -power, precision );
3535
; return base * mypow( base, power-1, precision );
3554
; if( base >= 0 ) return sqrt( base );
3555
; else return sqrt( -base );
3581
; 2^x = 1.000000001752 + x * (0.693146989552 + x * (0.2402298085906 + x * (5.54833215071e-2 + x * (9.67907584392e-3 + x * (1.243632065103e-3 + x * 2.171671843714e-4)))))
3582
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
3583
;In this case, our polynomial is the same degree, with error different by less than 1 bit, so it's just a waste to range-reduce in this way.
3586
;x-=int(x) ;leaves x in [0,1)
3588
;;if x==inf -> out==inf
3589
;;if x==-inf -> out==0
3590
;;if x==NAN -> out==NAN
3597
push af ;keep track of sign
3607
jr c,_pow_1 ;int(x)=0
3620
jr nz,exp_normalized
3631
jr exp_normalized ;.db $11 ;start of `ld de,**`
3638
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
3647
jp z,exp_underflow+1
3648
;perform 1-(var48+10)--> var48+10
3656
;our 'x' is at var48+10
3657
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
3658
;uses 14 bytes of RAM
3700
;-inf -> +0 because lim approaches 0 from the right
3722
;-inf -> +0 because lim approaches 0 from the right
3724
sbc a,a ;FF if should be 0,
3739
;---------------------------------------------------------------------------------------------------------
3741
;---------------------------------------------------------------------------------------------------------
3743
if defined MATH_SQR or defined MATH_EXP
3745
;Uses 3 bytes at scrap
3747
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
3766
jp z,sqrtSingle_special
3769
push af ;new exponent
3779
;AHL is the new remainder
3780
;Need to divide by 2, then divide by the 16-bit (var_x+4)
3784
;We are just going to approximate it
3866
;Output: DE is the sqrt, AHL is the remainder
3867
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
3891
jr _15a ;.db $FE ;start of `cp *`
3905
jr _16a ;.db $FE ;start of `cp *`
3919
jr _17a ;.db $FE ;start of `cp *`
3933
jr _18a ;.db $FE ;start of `cp *`
3937
;Now we have four more iterations
3938
;The first two are no problem
3950
jr _19a ;.db $FE ;start of `cp *`
3964
jr _20a ;.db $FE ;start of `cp *`
3969
;On the next iteration, HL might temporarily overflow by 1 bit
3971
rl d ;sla e \ rl d \ inc e
3975
adc hl,hl ;This might overflow!
3976
jr c,sqrt32_iter15_br0
3989
;On the next iteration, HL is allowed to overflow, DE could overflow with our current routine, but it needs to be shifted right at the end, anyways
3992
ld b,a ;either 0x00 or 0x80
4013
;returns A as the sqrt, HL as the remainder, D = 0
4027
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4038
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4049
dec d ;this resets the low bit of D, so `srl d` resets carry.
4050
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
4072
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4085
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4107
;---------------------------------------------------------------------------------------------------------
4109
;---------------------------------------------------------------------------------------------------------
4111
if defined MATH_LOG or defined MATH_LN
4114
; x / (1 + x/(2-x+4x/(3-2x+9x/(4-3x+16x/(5-4x)))))
4115
; a * x ^ (1/a) - a, where a = 100
4118
ld de, const_100_inv
4120
call powSingle ; temp = x ^ (1/100)
4124
call mulSingle ; temp1 = temp * 100
4127
call subSingle ; bc = temp1 - 100
4132
;---------------------------------------------------------------------------------------------------------
4134
;---------------------------------------------------------------------------------------------------------
4151
;---------------------------------------------------------------------------------------------------------
4153
;---------------------------------------------------------------------------------------------------------
4160
;;BC points to the output
4165
;;DE points to lg(y), HL points to x, BC points to output
4174
;---------------------------------------------------------------------------------------------------------
4176
; https://en.wikipedia.org/wiki/List_of_trigonometric_identities
4177
; https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions
4178
; https://cs.stackexchange.com/questions/89245/how-approximate-sine-using-taylor-series
4179
; https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
4180
;---------------------------------------------------------------------------------------------------------
4182
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4185
; taylor: x - x^3/6 + x^5/120 - x^7/5040
4186
; x(1 - x^2(1/6 - x^2(1/120 - x^2/5040)) )
4188
; var_b = round( x / (2*PI), 0 )
4189
; var_c = x - var_b*2*PI
4190
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4191
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4192
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4199
call copySingle ; return 0
4203
call trigRangeReductionSinCos
4208
call mulSingle ; var_b = var_a * var_a
4212
call mulSingle ; temp = x^2/5040
4216
call subSingle ; temp1 = 1/120 - temp
4220
call mulSingle ; temp = x^2 * temp1
4224
call subSingle ; temp1 = 1/6 - temp
4228
call mulSingle ; temp = x^2 * temp1
4232
call subSingle ; temp1 = 1 - temp
4236
call mulSingle ; return x * temp1
4239
trigRangeReductionSinCos:
4242
; var_b = round( x / (2*PI), 0 )
4250
; var_c = x - var_b*2*PI
4254
call mulSingle ; temp = var_b*2*PI
4258
call subSingle ; var_c = x - temp
4259
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4263
jr nc, trigRangeReductionSinCos.else.2
4266
call copySingle ; temp1 = var_c
4267
jr trigRangeReductionSinCos.endif.2
4268
trigRangeReductionSinCos.else.2:
4272
call addSingle ; temp1 = var_c + 2*PI
4273
trigRangeReductionSinCos.endif.2:
4274
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4278
jr c, trigRangeReductionSinCos.else.3
4279
jr z, trigRangeReductionSinCos.else.3
4283
call subSingle ; temp2
4284
jr trigRangeReductionSinCos.endif.3
4285
trigRangeReductionSinCos.else.3:
4288
call copySingle ; temp2 = temp1
4289
trigRangeReductionSinCos.endif.3:
4290
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4291
ld hl, const_half_pi
4294
jr c, trigRangeReductionSinCos.else.4
4295
jr z, trigRangeReductionSinCos.else.4
4299
call subSingle ; var_a
4300
jr trigRangeReductionSinCos.endif.4
4301
trigRangeReductionSinCos.else.4:
4304
call copySingle ; var_a = temp2
4305
trigRangeReductionSinCos.endif.4:
4306
; if( temp > PI, -1, 1 )
4310
jr nc, trigRangeReductionSinCos.endif.5
4314
ld (ix+2), a ; turn var_a to negative
4315
trigRangeReductionSinCos.endif.5:
4321
;---------------------------------------------------------------------------------------------------------
4323
;---------------------------------------------------------------------------------------------------------
4325
if defined MATH_COS or defined MATH_TAN
4328
; taylor: 1 - x^2/2 + x^4/24 - x^6/720
4329
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4330
; reduction: same as sin
4339
call copySingle ; return 1
4343
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4344
call trigRangeReductionSinCos
4349
call mulSingle ; var_b = var_a * var_a
4353
call mulSingle ; temp = x^2/720
4357
call subSingle ; temp1 = 1/24 - temp
4361
call mulSingle ; temp = x^2 * temp1
4365
call subSingle ; temp1 = 1/2 - temp
4369
call mulSingle ; temp = x^2 * temp1
4373
call subSingle ; temp1 = 1 - temp
4375
; temp3 = abs(var_c)
4376
; temp1 = temp1 * if( temp3 >= PI/2, -1, 1 ) ==> cos sign
4383
ld (ix+2), a ; temp3 = abs(var_c)
4385
ld de, const_half_pi
4386
call cmpSingle ; if temp3 >= PI/2 then temp1 = -temp1
4387
jr nc, cosSingle.endif.1
4391
ld (ix+2), a ; temp1 = -temp1
4395
call copySingle ; return temp1
4400
;---------------------------------------------------------------------------------------------------------
4402
;---------------------------------------------------------------------------------------------------------
4423
;---------------------------------------------------------------------------------------------------------
4425
;---------------------------------------------------------------------------------------------------------
4430
;taylor: x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4431
; x < -1: atan - PI/2
4432
; x >= 1: PI/2 - atan
4433
;reduction: abs(X) > 1 : Y = 1 / X
4434
; abs(X) <= 1: Y = X
4443
call copySingle ; return 0
4447
;x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4448
call trigRangeReductionAtan
4454
call mulSingle ; var_b = var_a * var_a
4458
call mulSingle ; temp = (4*x)^2
4462
call divSingle ; temp1 = temp/9
4466
call addSingle ; temp = 7 + temp1
4470
call mulSingle ; temp1 = var_b * 9
4474
call divSingle ; temp2 = temp1 / temp
4478
call addSingle ; temp = 5 + temp2
4482
call mulSingle ; temp1 = var_b * 4
4486
call divSingle ; temp2 = temp1 / temp
4490
call addSingle ; temp = 3 + temp2
4494
call divSingle ; temp2 = var_b / temp
4498
call addSingle ; temp = 1 + temp2
4502
call divSingle ; temp2 = var_a / temp
4504
; x >= 1: PI/2 - atan
4508
ld hl, const_half_pi
4515
; x < -1: atan - PI/2
4526
ld de, const_half_pi
4535
call copySingle ; return temp2
4538
trigRangeReductionAtan:
4539
;reduction: abs(X) > 1 : Y = 1 / X
4540
; abs(X) <= 1: Y = X
4549
ld (ix+2), a ; abs(x)
4553
jr nc, trigRangeReductionAtan.1
4559
jr trigRangeReductionAtan.2
4560
trigRangeReductionAtan.1:
4565
trigRangeReductionAtan.2:
4569
jr c, trigRangeReductionAtan.3
4573
ld (ix+2), a ; y = -y
4574
trigRangeReductionAtan.3:
4579
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4581
;---------------------------------------------------------------------------------------------------------
4583
;---------------------------------------------------------------------------------------------------------
4597
;---------------------------------------------------------------------------------------------------------
4599
;---------------------------------------------------------------------------------------------------------
4668
if defined MATH_ABSFN
4670
;---------------------------------------------------------------------------------------------------------
4672
;---------------------------------------------------------------------------------------------------------
4675
;;HL points to the float
4676
;;BC points to where to output the result
4695
;---------------------------------------------------------------------------------------------------------
4697
;---------------------------------------------------------------------------------------------------------
4700
;;HL points to the float
4701
;;BC points to where to output the result
4706
if defined powSingle or defined sgnSingle or defined MATH_NEG
4708
;---------------------------------------------------------------------------------------------------------
4710
;---------------------------------------------------------------------------------------------------------
4713
;;HL points to the float
4714
;;BC points to where to output the result
4720
jr nz, negSingle.test.sign
4723
jr nz, negSingle.test.sign
4726
jr nz, negSingle.test.sign
4729
jr nz, negSingle.test.sign
4740
negSingle.test.sign:
4743
jr z, negSingle.positive
4747
call negSingle.positive
4766
if defined MATH_DCOMP or defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN or defined MATH_SIN or defined MATH_TAN or defined MATH_COS or defined MATH_ATN
4768
;---------------------------------------------------------------------------------------------------------
4770
;---------------------------------------------------------------------------------------------------------
4773
;Input: HL points to float1, DE points to float2
4775
; float1 >= float2 : nc
4776
; float1 < float2 : c,nz
4777
; float1 == float2 : z
4778
; There is a margin of error allowed in the lower 2 bits of the mantissa.
4780
;Currently fails when both numbers have magnitude less than about 2^-106
4815
ld a,(scrap+3) ;new power
4816
pop bc ;B is old power
4826
or 1 ;not equal, so reset z flag
4827
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
4837
;---------------------------------------------------------------------------------------------------------
4839
;---------------------------------------------------------------------------------------------------------
4842
;Stores a pseudo-random number on [0,1)
4843
;it won't produce values on (0,2^-23)
4852
;DEHL is the mantissa, B is the exponent
4868
;If we needed to shift more than 8 bits, we'll load in more random data
4873
jp nc,rand_no_more_rand_data
4881
rand_no_more_rand_data:
4900
;;Tested and passes all CAcert tests
4901
;;Uses a very simple 32-bit LCG and 32-bit LFSR
4902
;;it has a period of 18,446,744,069,414,584,320
4903
;;roughly 18.4 quintillion.
4904
;;LFSR taps: 0,2,6,7 = 11000101
4906
;;Thanks to Runer112 for his help on optimizing the LCG and suggesting to try the much simpler LCG. On their own, the two are terrible, but together they are great.
4907
;Uses 64 bits of state
4943
if defined MATH_FOUT
4945
;---------------------------------------------------------------------------------------------------------
4947
; in HL = Single address
4948
; BC = String address
4949
; out A = String size
4950
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
4951
; http://0x80.pl/articles/convert-float-to-integer.html
4952
;---------------------------------------------------------------------------------------------------------
4966
; Move the float to scrap
4970
; Make the float negative, write a '-' if already negative
4979
ld a,'-' ; write '-' simbol
4987
; Check if the exponent field is 0 (a special value)
4994
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
5002
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
5010
ld (pow10exp_single),a ;The base-10 exponent
5014
ld de,pow10LUT ;get the table of 10^-(2^k)
5016
ld hl, pow10exp_single
5018
call singletostr_mul
5019
call singletostr_mul
5020
call singletostr_mul
5021
call singletostr_mul
5022
call singletostr_mul
5023
call singletostr_mul
5024
;now the number is pretty close to a nice value
5026
; If it is less than 1, multiply by 10
5031
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
5037
ld hl,pow10exp_single
5043
; Convert to a fixed-point number !
5057
;We need to get 7 digits
5059
pop hl ;Points to the string
5061
;The first digit can be as large as 20, so it'll actually be two digits
5065
;Increment the exponent :)
5066
ld de,(pow10exp_single-1)
5068
ld (pow10exp_single-1),de
5077
; Get the remaining digits.
5084
call singletostrmul10
5089
;Save the pointer to the end of the string
5096
jr c,rounding_done_single
5097
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
5106
rounding_done_single:
5109
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
5121
;Now lets move HL-DE bytes at DE+1 to DE
5133
;If z flag is reset, this means that the exponent should be bumped up 1
5134
ld a,(pow10exp_single)
5137
ld (pow10exp_single),a
5140
;if -4<=A<=6, then need to insert the decimal place somewhere.
5145
;for this, we need to insert the decimal after the first digit
5146
;Then, we need to append the exponent string
5148
ld de,strout_single-1
5150
cp '-' ;negative sign
5158
;remove any stray zeroes at the end before appending the exponent
5162
; Write the exponent
5165
ld a,(pow10exp_single)
5168
ld (hl),'-' ;negative sign
5186
ld de, strout_single
5189
ld a, l ; string size
5191
ld hl,strout_single-1
5195
ld a,(pow10exp_single)
5199
;need to put zeroes before everything
5202
cp '-' ;negative sign
5228
ld de,strout_single-1
5232
cp '-' ;negative sign
5243
ld hl,strout_single-1
5261
;multiply the 0.24 fixed point number at scrap by 10
5262
;overflow in A register
5297
;Check that the last digit isn't a decimal!
5351
;---------------------------------------------------------------------------------------------------------
5353
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
5354
;---------------------------------------------------------------------------------------------------------
5359
ptr_sto: equ scrap+9
5361
;;#Routines/Single Precision
5363
;; HL points to the string
5364
;; BC points to where the float is output
5366
;; scrap+9 is the pointer to the end of the string
5368
;; 11 bytes at scrap ?
5373
;Check if there is a negative sign.
5382
;Skip all leading zeroes
5385
jr z,$-4 ;jumps back to the `inc hl`
5388
;Check if the next char is char_DEC
5390
or a ;to reset the carry flag
5392
jr _54a ;.db $FE ;start of cp *
5399
jr z,$-5 ;jumps back to the `dec b`
5402
;Now we read in the next 8 digits
5408
;Now `scrap` holds the 4-digit base-100 number.
5410
;if carry flag is set, just need to get rid of remaining digits
5411
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
5422
jp z,strToSingle_inf
5425
;Now check for engineering `E` to modify the exponent
5429
;Gotta multiply the number at (scrap) by 2^24
5432
call scrap_times_256
5435
call scrap_times_256
5438
call scrap_times_256
5441
call scrap_times_256
5444
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
5452
jp z,strToSingle_zero-1
5456
jp m,strToSingle_normed
5457
;Will need to iterate at most three times
5470
;Move the number to scrap
5479
;now (scrap) is our number, need to multiply by power of 10!
5480
;Power of 10 is stored in B, need to put in A first
5488
jp nc,strToSingle_inf+1
5491
jp nc,strToSingle_zero
5515
cp char_NEG ;negative exponent?
5567
call scrap_times_sub
5580
jr nz,strToSingle_inf
5598
if defined roundSingle or defined MATH_FRCSGL
5600
;---------------------------------------------------------------------------------------------------------
5602
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
5603
;---------------------------------------------------------------------------------------------------------
5610
ld l, (ix) ; convert integer parameter to single float
5612
ld bc, 0x1000 ; bynary digits count + sign
5614
int2Single.test.zero:
5616
or h ; test if hl is not zero
5617
jr nz, int2Single.test.negative
5619
jr nz, int2Single.test.negative
5624
int2Single.test.negative:
5625
bit 7, h ; test if hl is negative
5626
jr z, int2Single.normalize
5627
ld c, 0x80 ; sign negative
5636
int2Single.normalize:
5639
jr nz, int2Single.mount
5642
jr int2Single.normalize
5645
res 7, h ; turn off upper bit
5647
ld a, c ; restore sign
5649
ld h, a ; ...into upper mantissa
5651
ld e, h ; sign+mantissa
5652
ld h, l ; high mantissa
5653
ld l, 0 ; low mantissa
5655
ld a, b ; binary digits count
5656
or 0x80 ; exponent bias
5661
ld (ix), l ; low mantissa
5662
ld (ix+1), h ; high mantissa
5663
ld (ix+2), e ; sign + mantissa
5664
ld (ix+3), d ; expoent
5673
if defined roundSingle or defined MATH_FRCINT
5675
;---------------------------------------------------------------------------------------------------------
5677
; http://0x80.pl/articles/convert-float-to-integer.html
5678
;---------------------------------------------------------------------------------------------------------
5681
; HL points to the single-precision float
5683
; HL is the 16-bit signed integer part of the float
5684
; BC points to 16-bit signed integer
5701
jr c,no_shift_single_to_int16
5703
jr nc,no_shift_single_to_int16
5725
jr _67a ;.db $11 ;start of ld de,*
5737
no_shift_single_to_int16:
5759
;---------------------------------------------------------------------------------------------------------
5760
; Auxiliary routines
5761
;---------------------------------------------------------------------------------------------------------
5768
const_pi: db $DB,$0F,$49,$81
5769
const_e: db $54,$f8,$2d,$81
5770
const_lg_e: db $3b,$AA,$38,$80
5771
const_ln_2: db $18,$72,$31,$7f
5772
const_log2: db $9b,$20,$1a,$7e
5773
const_lg10: db $78,$9a,$54,$81
5774
const_0: db $00,$00,$00,$00
5775
const_1: db $00,$00,$00,$80
5776
const_2: dw 0, 33024
5777
const_3: dw 0, 33088
5778
const_4: dw 0, 33280
5779
const_5: dw 0, 33312
5780
const_7: dw 0, 33376
5781
const_9: dw 0, 33552
5782
const_16: dw 0, 33792
5783
const_100: db $00,$00,$48,$86
5784
const_100_inv: dw 55050, 31011
5785
const_precision: db $77,$CC,$2B,$65 ;10^-8
5786
const_half_1: dw 0, 32512
5787
const_inf: db $00,$00,$40,$00
5788
const_NegInf: db $00,$00,$C0,$00
5789
const_NaN: db $00,$00,$20,$00
5790
const_log10_e: db $D9,$5B,$5E,$7E
5791
const_2pi: db $DB,$0F,$49,$82
5792
const_2pi_inv: db $83,$F9,$22,$7D
5793
const_half_pi: dw 4059, 32841
5794
const_p25: db $00,$00,$00,$7E
5795
const_p5: db $00,$00,$00,$7F
5798
sin_a1: dw 43691, 32042
5799
sin_a2: dw 34952, 30984
5800
sin_a3: dw 3329, 29520
5801
cos_a1: equ const_half_1
5802
cos_a2: dw 43691, 31530
5803
cos_a3: dw 2914, 30262
5804
exp_a1: db $15,$72,$31,$7F ;.693146989552
5805
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
5806
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
5807
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
5808
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
5809
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
5810
const_1p40625: db $00,$00,$34,$80 ;1.40625
5812
if defined MATH_CONSTSINGLE
5820
;A is the constant ID#
5821
;returns nc if failed, c otherwise
5822
;HL points to the constant
5823
cp (end_const-start_const)>>2
5830
;#if ((end_const-4)>>8)!=(start_const>>8)
5843
db $CD,$CC,$4C,$7C ;.1
5844
db $0A,$D7,$23,$79 ;.01
5845
db $17,$B7,$51,$72 ;.0001
5846
db $77,$CC,$2B,$65 ;10^-8
5847
db $95,$95,$66,$4A ;10^-16
5848
db $1F,$B1,$4F,$15 ;10^-32
5851
db $00,$00,$20,$83 ;10
5852
db $00,$00,$48,$86 ;100
5853
db $00,$40,$1C,$8D ;10000
5854
db $20,$BC,$3E,$9A ;10^8
5855
db $CA,$1B,$0E,$B5 ;10^16
5856
db $AE,$C5,$1D,$EA ;10^32
5863
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
5864
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
5865
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
5866
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
5867
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
5868
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
5869
;C>=2 15+{0,20+{0,8}}
5872
;avg: 349.21279907227cc
5963
;26 bytes, adds 118cc to the traditional routine
5998
;c flag means don't increment the exponent
6001
jr c,ascii_to_uint8_noexp
6003
jr z,ascii_to_uint8_noexp-2
6007
jr nc,ascii_to_uint8_noexp_end
6019
jr z,ascii_to_uint8_noexp_2nd
6023
jr nc,ascii_to_uint8_noexp_end
6034
ascii_to_uint8_noexp:
6037
jr nc,ascii_to_uint8_noexp_end
6044
ascii_to_uint8_noexp_2nd:
6049
jr nc,ascii_to_uint8_noexp_end
6052
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
6053
ascii_to_uint8_noexp_end:
6063
if defined MATH_RSUBSINGLE
6084
jp addInject ;jumps in to the addSingle routine
6088
if defined MATH_MOD1SINGLE
6090
;This routine performs `x mod 1`, returning a non-negative value.
6113
jr z,mod1Single_special
6126
;If it is zero, need to set exponent to zero and return
6149
;make sure it isn't zero else we need to add 1
6161
;If INF, need to return NaN instead
6162
;For 0 and NaN, just return itself :)
6182
if defined MATH_FOUT
6184
; --------------------------------------------------------------
6185
; Converts a signed integer value to a zero-terminated ASCII
6186
; string representative of that value (using radix 10).
6188
; Brandon Wilson WikiTI
6189
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
6190
; --------------------------------------------------------------
6192
; HL Value to convert (two's complement integer).
6193
; DE Base address of string destination. (pointer).
6194
; --------------------------------------------------------------
6197
; --------------------------------------------------------------
6198
; REGISTERS/MEMORY DESTROYED
6200
; --------------------------------------------------------------
6206
; Detect sign of HL.
6210
; HL is negative. Output '-' to string and negate HL.
6215
; Negate HL (using two's complement)
6219
ld a, 0 ; Note that XOR A or SUB A would disturb CF
6223
; Convert HL to digit characters
6225
ld b, 0 ; B will count character length of number
6228
call div_hl_c; HL = HL / A, A = remainder
6235
; Retrieve digits from stack
6243
; Terminate string with NULL
6254
ld a, l ; string size
6262
;===============================================================
6263
; Convert a string of base-10 digits to a 16-bit value.
6264
; http://z80-heaven.wikidot.com/math#toc32
6266
; DE points to the base 10 number string in RAM.
6268
; HL is the 16-bit value of the number
6269
; DE points to the byte after the number
6274
; A (actually, add 30h and you get the ending token)
6277
; n is the number of digits
6279
; at most 595 cycles for any 16-bit decimal value
6280
;===============================================================
6283
ld hl,0 ; 10 : 210000
6300
jr nc,ConvLoop ;12|23: 30EE
6302
jr ConvLoop ; --- : 18EB
6309
; return remainder in a
6310
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
6331
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
6361
djnz div_dehl_c.loop
6369
;---------------------------------------------------------------------------------------------------------
6370
; VARIABLES INITIALIZE
6371
;---------------------------------------------------------------------------------------------------------
6375
ld (VAR_DUMMY.COUNTER), a ; max circular queue = 8 dummys
6376
ld hl, VAR_DUMMY.DATA ; start of variable dummy circular queue
6377
ld (VAR_DUMMY.POINTER), hl
6378
ld b, VAR_DUMMY.LENGTH
6383
djnz INITIALIZE_DUMMY.1
6388
ld (BASIC_DATPTR), hl ; next DATA pointer to use by READ command
6390
ld (BASIC_DATLIN), hl ; index of DATA item to use by READ command
6393
INITIALIZE_VARIABLES:
6394
call INITIALIZE_DATA
6395
call INITIALIZE_DUMMY
6398
call gfxInitSpriteCollisionTable
6401
;if defined COMPILE_TO_ROM
6402
; ld ix, BIOS_JIFFY ; initialize rom clock
6410
ld d, 2 ; any = default integer
6411
ld c, 0 ; variable name 1 (variable number)
6412
ld b, 0 ; variable name 2 (type flag=any)
6413
call INIT_VAR ; variable initialize
6415
ld d, 2 ; any = default integer
6416
ld c, 1 ; variable name 1 (variable number)
6417
ld b, 0 ; variable name 2 (type flag=any)
6418
call INIT_VAR ; variable initialize
6420
ld d, 2 ; any = default integer
6421
ld c, 2 ; variable name 1 (variable number)
6422
ld b, 0 ; variable name 2 (type flag=any)
6423
call INIT_VAR ; variable initialize
6425
ld d, 2 ; any = default integer
6426
ld c, 3 ; variable name 1 (variable number)
6427
ld b, 0 ; variable name 2 (type flag=any)
6428
call INIT_VAR ; variable initialize
6430
ld d, 2 ; any = default integer
6431
ld c, 4 ; variable name 1 (variable number)
6432
ld b, 0 ; variable name 2 (type flag=any)
6433
call INIT_VAR ; variable initialize
6435
ld d, 2 ; any = default integer
6436
ld c, 5 ; variable name 1 (variable number)
6437
ld b, 0 ; variable name 2 (type flag=any)
6438
call INIT_VAR ; variable initialize
6442
;---------------------------------------------------------------------------------------------------------
6443
; MAIN WORK AREA - LITERALS / VARIABLES / CONFIGURATIONS
6444
;---------------------------------------------------------------------------------------------------------
6446
if defined COMPILE_TO_ROM
6449
pgmPage1.pad: equ pageSize - (workAreaPad - pgmArea)
6451
if pgmPage1.pad >= 0
6454
; .WARNING "There's no free space left on program page 1"
6459
VAR_STACK.START: equ ramArea
6460
;VAR_STACK.END: equ VAR_STACK.START + 0x800 ; 2kb (~200 variables)
6462
VAR_STACK.POINTER: equ VAR_STACK.START
6464
PRINT.CRLF: db 3, 0, 0, 2
6465
dw PRINT.CRLF.DATA, 0, 0, 0
6466
PRINT.CRLF.DATA: db 13,10,0
6468
PRINT.TAB: db 3, 0, 0, 1
6469
dw PRINT.TAB.DATA, 0, 0, 0
6470
PRINT.TAB.DATA: db 09,0
6473
LIT_NULL_DBL: dw 0, 0, 0, 0
6479
LIT_QUOTE_CHAR: db '\"'
6482
LIT_TRUE: db 2, 0, 0
6486
LIT_FALSE: db 2, 0, 0
6491
IDF_4: equ VAR_STACK.POINTER + 0
6498
IDF_6: equ VAR_STACK.POINTER + 11
6505
IDF_10: equ VAR_STACK.POINTER + 22
6512
IDF_12: equ VAR_STACK.POINTER + 33
6531
IDF_20: equ VAR_STACK.POINTER + 44
6538
IDF_22: equ VAR_STACK.POINTER + 55
6540
; double decimal literal
6542
dw 4059, 33097, 0, 0
6543
;LIT_23_DATA: db '3.1415926536',0
6546
LIT_26: db 3, 0, 0, 1
6547
dw LIT_26_DATA, 0, 0
6549
LIT_26_DATA: db " ", 0
6555
AFTER_LAST_VARIABLE: equ VAR_STACK.POINTER + 66
6557
VAR_DUMMY.START: equ AFTER_LAST_VARIABLE ; variable dummy circular queue area
6558
VAR_DUMMY.COUNTER: equ VAR_DUMMY.START ; variable dummy circular queue count
6559
VAR_DUMMY.POINTER: equ VAR_DUMMY.COUNTER + 1 ; pointer to next variable dummy
6560
VAR_DUMMY.DATA: equ VAR_DUMMY.POINTER + 2 ; first variable dummy
6562
VAR_DUMMY.SIZE: equ 8
6563
VAR_DUMMY.LENGTH: equ (11 * VAR_DUMMY.SIZE)
6564
VAR_DUMMY.END: equ VAR_DUMMY.DATA + VAR_DUMMY.LENGTH
6565
VAR_STACK.END: equ VAR_DUMMY.END + 1
6567
;--------------------------------------------------------
6569
;--------------------------------------------------------
6572
DATA_ITEMS_COUNT: equ 0
6574
DATA_SET_ITEMS_START:
6575
DATA_SET_ITEMS_COUNT: equ 0
6578
;---------------------------------------------------------------------------------------------------------
6580
;---------------------------------------------------------------------------------------------------------
6582
if defined COMPILE_TO_ROM
6586
pgmPage2.pad: equ romSize - (romPad - pgmArea)
6588
if pgmPage2.pad >= 0
6591
if pgmPage2.pad < lowLimitSize
6592
.WARNING "There's only less than 5% free space on this ROM"
6595
.ERROR "There's no free space left on this ROM"
6600
end_file: end start_pgm ; label start is the entry point